第 9 章 可观测性:Agent 不是黑盒
第 9 章 可观测性:Agent 不是黑盒
第 1 章我们说过,纯 Agent 的致命问题之一是"调试像黑盒"。这一章正面解决它。当一个 Agent 在生产环境跑起来,你怎么知道它没在犯错?怎么定位"客服答错"到底是模型幻觉、工具数据错误、还是路由决策错了?答案是——可观测性。ADK 把这件事做成了内置能力。
9.1 为什么 Agent 比传统服务更难观测
传统的 Web 服务,可观测性很成熟:日志记请求,指标记延迟,追踪记调用链。但 Agent 带来三个新挑战:
第一,LLM 的不确定性。 传统服务是确定性的——同样的输入,同样的输出。Agent 不一样,同样的用户问题,模型每次的推理路径都可能不同。你无法靠"复现"来定位问题,必须靠观测。
第二,工具调用的副作用。 Agent 会调用工具(查数据库、调 API、发消息),每个工具调用都有真实副作用。工具调错了,可能造成实际损失。
第三,多步决策链。 一个客服回答,背后可能是:理解意图 → 调订单工具 → 读结果 → 生成回复。任何一步错了,最终答案就错了。你需要的不是"这个回答对不对",而是"哪一步走错了"。
ADK 对这三个挑战的回应是:内置的、基于 OpenTelemetry 标准的可观测性,通过日志、指标、追踪三根支柱全面覆盖。
9.2 三支柱总览
ADK 的可观测性遵循 OpenTelemetry(OTel)语义约定,使用标准 OTLP 格式发射数据,可以无缝接入任何 OTel 兼容后端(Prometheus、Datadog、SigNoz、Google Cloud Monitoring/Trace/Logging、Jaeger、Grafana Tempo)。
三个支柱的分工:
| 支柱 | 回答的问题 | 形态 |
|---|---|---|
| 日志(Logs) | 发生了什么(what happened) | 详细的叙事记录 |
| 指标(Metrics) | 多久发生一次、有多快(how often / how fast) | 聚合的定量数据 |
| 追踪(Traces) | 时间花在哪里、层级关系 | 瀑布视图的调用链 |
三个支柱的配置入口统一是 maybe_set_otel_providers():
from google.adk.telemetry.setup import maybe_set_otel_providers
import os
os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "http://your-collector:4318"
os.environ["OTEL_SERVICE_NAME"] = "yunxiao-agent"
os.environ["OTEL_RESOURCE_ATTRIBUTES"] = "env=prod,region=cn-east"
maybe_set_otel_providers()也可以用 CLI 方式(adk web / adk api_server 支持 --otel_to_cloud 标志导出到 Google Cloud):
adk web --otel_to_cloud path/to/your/agents_dir厂商中立:ADK 不会把你锁死在某个监控管道上。你可以自己实例化标准 OTel meter provider / tracer provider,导出到任意基础设施。
9.3 日志(Logs):发生了什么
9.3.1 设计理念
ADK 日志默认"详细但不冗长",由应用开发者自行配置。它使用宿主语言的标准日志设施(Python 的 logging 模块),并记录结构化的 GenAI 事件(遵循 OTel 语义约定)。
一个重要安全默认:默认情况下,提示词内容(prompt content)会在日志中被省略,以保护安全,需要显式开启。
9.3.2 日志级别
| 级别 | 记录的信息 |
|---|---|
| DEBUG | 完整 LLM 提示词(含系统指令、历史、工具)、详细 API 响应、内部状态 |
| INFO | agent 生命周期、session 创建/删除、工具执行(名称和参数) |
| WARNING | 弃用功能、已恢复的非致命错误 |
| ERROR | 外部服务调用失败、未捕获异常、配置错误 |
生产环境推荐
INFO或WARNING;只在排障时开DEBUG(DEBUG 可能包含敏感信息)。
9.3.3 开启日志
程序化开启 DEBUG 日志:
import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(name)s - %(message)s'
)CLI 方式(adk web 支持 --log_level):
adk web --log_level DEBUG path/to/your/agents_dir开启完整 prompt 内容捕获(默认省略,需显式开启):
export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true更精细的方式是只对单次运行开启(推荐,作用域到单次 run 而不是整个进程):
from google.adk.agents.run_config import RunConfig
from google.adk.telemetry import ContentCapturingMode, TelemetryConfig
run_config = RunConfig(
telemetry=TelemetryConfig(
capture_message_content=ContentCapturingMode.SPAN_AND_EVENT,
),
)9.3.4 日志导出
导出到 OTLP 兼容后端:
import os
from google.adk.telemetry.setup import maybe_set_otel_providers
os.environ["OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"] = "http://your-collector:4318/v1/logs"
os.environ["OTEL_SERVICE_NAME"] = "yunxiao-agent"
maybe_set_otel_providers()9.3.5 日志解读
一条典型的 ADK 日志:
2025-07-08 11:22:33,456 - DEBUG - google_adk.google.adk.models.google_llm - LLM Request: contents { ... }- 时间戳:
2025-07-08 11:22:33,456 - 级别:
DEBUG - logger 名称:
google_adk.google.adk.models.google_llm(所有 logger 是google_adk的子 logger) - 消息:
LLM Request: contents { ... }
调试时重点关注 google_adk.google.adk.models.google_llm 这个 logger,它可以验证:系统指令是否正确、对话历史是否准确、提供的工具是否正确、模型是否正确调用了工具。
9.4 指标(Metrics):多久一次、有多快
9.4.1 关键指标
ADK 自动 instrument agent 生命周期、workflow 步骤和工具执行。核心指标:
| 指标 | 类型 | 描述 | 关键维度 |
|---|---|---|---|
gen_ai.invoke_agent.duration |
Histogram | agent 处理一个 prompt 的总耗时 | agent.name, error.type |
gen_ai.invoke_workflow.duration |
Histogram | 运行一个 workflow 的耗时 | workflow.name, error.type |
gen_ai.execute_tool.duration |
Histogram | 单个工具的执行延迟(找慢 API) | tool.name, error.type |
gen_ai.invoke_agent.inference_calls |
Histogram | 一次 agent 调用中的模型调用次数 | agent.name |
gen_ai.invoke_agent.tool_calls |
Histogram | 一次 agent 调用中的工具调用次数 | agent.name |
gen_ai.client.operation.duration |
Histogram | 单次模型调用的延迟 | request.model, error.type |
gen_ai.client.token.usage |
Histogram | 每次模型调用的 token 消耗 | token.type(输入/输出) |
小结:ADK 追踪 LLM 应用最关键的信号——token 消耗、请求延迟、工具执行可靠性。
9.4.2 导出指标
import os
from google.adk.telemetry.setup import maybe_set_otel_providers
os.environ["OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"] = "http://your-collector:4318/v1/metrics"
os.environ["OTEL_SERVICE_NAME"] = "yunxiao-agent"
maybe_set_otel_providers()9.5 追踪(Traces):时间花在哪里
9.5.1 核心 span
追踪把事件连接起来,显示请求在 agent 架构中的端到端旅程和层级关系。核心 span:
| Span | 描述 |
|---|---|
invoke_agent {agent.name} |
一次 agent 调用的生命周期(根 span) |
invoke_workflow {workflow.name} |
多步 agentic workflow 的调用 |
execute_tool {tool.name} |
某个工具/函数调用的执行 |
generate_content {model.name} |
底层模型生成内容的调用 |
Span 组织成瀑布视图:agent run 是根 span,包含 LLM 操作的子 span,LLM span 又包含工具执行的子 span。ADK 还支持上下文传播——自动跨进程边界传递 trace 上下文,外部微服务 span 会链接到 agent 根 trace。
9.5.2 导出追踪
import os
from google.adk.telemetry.setup import maybe_set_otel_providers
os.environ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = "http://your-collector:4318/v1/traces"
os.environ["OTEL_SERVICE_NAME"] = "yunxiao-agent"
maybe_set_otel_providers()导出到 Google Cloud Trace:
from google.adk.telemetry.google_cloud import get_gcp_exporters
from google.adk.telemetry.setup import maybe_set_otel_providers
import os
gcp_exporters = get_gcp_exporters(enable_cloud_tracing=True)
os.environ["OTEL_SERVICE_NAME"] = "yunxiao-agent"
maybe_set_otel_providers([gcp_exporters])get_gcp_exporters 有三个开关:enable_cloud_logging、enable_cloud_metrics、enable_cloud_tracing,分别对应日志/指标/追踪。
9.6 事件流:ADK 可观测性的基础
9.6.1 事件循环是 Runtime 的心脏
ADK 的 Runner 和执行逻辑(Agent、LLM 调用、工具)之间通过 Event 对象来回通信:
Runner收到用户查询,调用 Agent 开始处理- Agent 运行到有东西要"报告"时,yield(产出)一个 Event
- Runner 收到 Event,处理相关动作(通过 Services 保存状态变化),并转发出去
- Agent 逻辑只在 Runner 处理完事件后恢复执行
- 循环往复,直到 Agent 不再产生事件
每个决策都是一个事件——这就是第 3 章讲的"事件流让 Agent 可观测"的机制基础。
9.6.2 状态提交的时机
一个重要细节:在 Agent/工具/回调中修改 ctx.session.state 只是本地记录,只有在包含对应 state_delta 的 Event 被 yield 并被 Runner 处理后,修改才保证持久化。这个机制保证了状态变更基于完整响应原子性地应用。
9.6.3 ADK Web UI 的 Trace 视图
adk web UI 自带 Trace 标签页,是调试利器:
- Traces 自动按用户消息分组
- 每行可交互:悬停高亮对应聊天消息,点击打开详情面板
- 详情面板四个标签页:Event(原始事件)、Request(发给模型的请求)、Response(模型响应)、Graph(工具调用与 agent 逻辑流程图)
9.7 主线项目:给云销客服加可观测性
9.7.1 配置目标
给云销客服配置可观测性,目标:
- 日志:记录工具调用、agent 生命周期
- 指标:监控客服响应延迟、token 消耗、工具调用次数
- 追踪:完整链路,定位"客服答错"的根因
9.7.2 代码配置
import logging
import os
from google.adk.telemetry.setup import maybe_set_otel_providers
# 1. 开启结构化日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(name)s - %(message)s'
)
# 2. 配置 OTLP 导出(假设你有一个 OTel collector)
os.environ["OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"] = "http://collector:4318/v1/logs"
os.environ["OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"] = "http://collector:4318/v1/metrics"
os.environ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = "http://collector:4318/v1/traces"
os.environ["OTEL_SERVICE_NAME"] = "yunxiao-agent"
os.environ["OTEL_RESOURCE_ATTRIBUTES"] = "service=yunxiao,env=prod"
maybe_set_otel_providers()9.7.3 定位一次"客服答错"的根因
假设用户投诉:"我说要退一个电子产品,客服说可以退,但政策明明写着已激活的不能退。"
用可观测性排查的路径:
第一步:看日志。找到那次会话的 INFO 日志,看 Agent 调了哪些工具、传了什么参数。
第二步:看追踪的瀑布图。定位这次回答的完整链路:
invoke_agent yunxiao_cs_agent(根 span)generate_content gemini-flash-latest(模型判断)execute_tool get_refund_policy(工具调用)generate_content(生成回复)
第三步:逐个 span 检查。发现 get_refund_policy 收到的 product_category 参数是 "electronics",但返回的政策里 condition: "商品未拆封"——而模型在回复里忽略了"已激活的不支持退货"这条 note。
结论:不是工具数据错(数据正确),而是模型生成回复时漏掉了政策里的排除条款。修复方向:调整 get_refund_policy 的返回结构,把"禁止条款"单独强调,或者加强指令。
这个排查路径,就是可观测性的价值——没有它,你只能对着一个错误答案干瞪眼。
「为什么 ADK 这样设计」:可观测性是生产 Agent 的入场券
为什么 ADK 要把可观测性做成内置能力,而不是留给开发者自己接?
因为"Agent 不可观测"是生产事故的温床。想想没有可观测性的后果:
- 客服答错了,你不知道是模型幻觉、工具错、还是路由错
- 系统变慢了,你不知道是 LLM 慢、工具慢、还是并发瓶颈
- token 成本暴涨,你不知道是哪个 Agent 在烧钱
而这些问题的共性:你无法在问题发生前预防,只能在发生后靠猜。可观测性的本质,是把"靠猜"变成"看得见"。
ADK 的选择是:把日志、指标、追踪做成框架的原生能力,用 OpenTelemetry 标准对接一切后端。这样:
- 开发者零成本获得可观测性(默认就记录)
- 不锁定后端(OTel 标准,接谁都可以)
- 事件流本身就可观测(每个决策都是事件)
这和第 1 章"生产优先"的定位一脉相承——一个不能观测的 Agent,不配进入生产。
本章小结
- 三支柱:日志(发生了什么)、指标(多久一次/多快)、追踪(时间花在哪里/层级关系)
- 标准先行:基于 OpenTelemetry 语义约定 + OTLP 格式,厂商中立,可接任何后端
- 日志:默认省略 prompt 内容(安全),
DEBUG/INFO/WARNING/ERROR四级,--log_level或logging.basicConfig配置 - 指标:
gen_ai.*系列——延迟、token、工具调用次数、agent 耗时 - 追踪:
invoke_agent/execute_tool/generate_contentspan,瀑布视图 + 上下文传播 - 事件流是基础:每个决策都是事件,Runner 的事件循环是 Runtime 的心脏
- Web UI Trace 视图:Event/Request/Response/Graph 四标签调试
- 排查实战:用日志 + 追踪瀑布图定位"客服答错"根因
练习
- 开启日志:给云销客服开
DEBUG日志,跑一次对话,观察google_llmlogger 输出了什么。 - 本地起 collector:如果你有 Docker,跑一个本地 OTel collector(如 Jaeger),把云销客服的 traces 导出过去,用瀑布图看一次完整回答的链路。
- 制造问题再排查:故意让
get_refund_policy返回错误数据,用可观测性流程定位"客服答错"的根因,体验排查路径。
下一章预告:第 10 章,可观测性解决"怎么看",评估解决"怎么判断好坏"。ADK 的评估框架,让 Agent 的质量可以量化、可以回归。