Vision-Agents 可观测性实战:用 Prometheus + Grafana 采集并可视化语音与视觉 Agent 的实时指标
【免费下载链接】Vision-AgentsOpen Vision Agents by Stream. Build voice and vision agents quickly with any model or video provider. Uses Stream's edge network for ultra-low latency.项目地址: https://gitcode.com/GitHub_Trending/vi/Vision-Agents
本篇技术指南以 Vision-Agents 仓库的 06_prometheus_metrics_example 示例为核心,讲解如何通过 OpenTelemetry 将运行中的语音/视觉 Agent(LLM、STT、TTS、语音轮次检测、Realtime LLM 等组件)的指标实时导出到 Prometheus,并在 Grafana 中借助预置仪表盘进行可视化监控。读完本文,你将掌握在 Vision-Agents 上启用指标采集的完整链路:从应用内配置 OpenTelemetry 的 Prometheus 导出器,到编写 Prometheus 抓取配置,再到部署开箱即用的 Grafana 看板,并理解MetricsCollector在源码层的自动挂载与合并原理。
示例概览:一个可观测的实时语音 Agent
该示例构建了一个基于 Gemini LLM + Deepgram STT + ElevenLabs TTS + GetStream Edge 的实时语音 Agent。它除了演示基本的"问天气"对话能力外,核心目的是展示指标采集能力:Agent 内部的 LLM、STT、TTS、Turn Detection 等组件在通话过程中会自动产生结构化指标,并在http://localhost:9464/metrics以 Prometheus 文本格式暴露出来。
示例的三大目标(与 README 声明一致):
- 用 OpenTelemetry 的 Prometheus 导出器完成指标出口配置;
- 在实时视频/语音通话期间从
/metrics端点抓取指标; - 用预置 Grafana 仪表盘可视化指标。
整个示例的关键设计是:指标采集默认开启。正如示例源码注释(prometheus_metrics_example.py)与核心库文档(observability/metrics.py)所述,每个Agent在构造时都会自动创建一个MetricsCollector,因此只要应用配置好 OpenTelemetry 的 MeterProvider,指标就会自动落盘,无需逐组件手动埋点。
快速开始:两种接入方式
方式一:仅暴露指标端点(最快验证)
cd examples/06_prometheus_metrics_example uv sync uv run python prometheus_metrics_example.py run启动后打开 http://localhost:9464/metrics,即可在与 Agent 对话的同时实时观察原始指标文本。uv sync会根据该目录下的 pyproject.toml 解析依赖,其中vision-agents及其各插件均通过[tool.uv.sources]以可编辑方式(editable)指向仓库本体的agents-core与plugins/目录。
依赖清单中与可观测性直接相关的包:
opentelemetry-api/opentelemetry-sdk:OpenTelemetry 指标 API 与 SDK;opentelemetry-exporter-prometheus:将 OTel 指标桥接到 Prometheus 格式的导出器;prometheus-client:提供start_http_server,用于启动/metricsHTTP 服务。
方式二:完整可观测性栈(Prometheus + Grafana)
# 1. 启动 Prometheus + Grafana 容器 cd examples/06_prometheus_metrics_example docker compose up -d # 2. 运行 Agent(另开终端) uv sync uv run python prometheus_metrics_example.py run # 3. 打开 Grafana # http://localhost:3000 (无需登录,已启用匿名访问)Grafana 预置仪表盘会展示以下指标面板(README 明确列出):
- LLM 延迟(p50 / p95 / p99)
- STT 延迟(p50 / p95 / p99)
- TTS 延迟(p50 / p95 / p99)
- 轮次时长(Turn duration)
- 轮次尾部静默时长(Turn trailing silence)
- 错误率(Error rates)
结束时停止观测栈:
docker compose down数据流架构
README 给出了简洁的架构图,本文稍作注释保留:
┌─────────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Stream Agent │────▶│ Prometheus │────▶│ Grafana │ │ (port 9464) │ │ (port 9090) │ │ (port 3000) │ │ │ │ │ │ │ │ Metrics endpoint: │ │ Scrapes every │ │ Pre-built │ │ /metrics │ │ 5 seconds │ │ dashboards │ └─────────────────────┘ └─────────────────┘ └─────────────────┘Agent 进程内的 Prometheus HTTP Server(9464 端口)是被抓取目标,Prometheus 容器以 5 秒间隔抓取,Grafana 通过预置数据源与仪表盘完成可视化(详见 docker-compose.yml 与 prometheus.yml)。
可用指标全景:五类核心指标 + 两类进阶指标
LLM 指标
| 指标名(Prometheus 导出后) | 类型 | 含义 |
|---|---|---|
llm_latency_ms_milliseconds | histogram | LLM 完整响应延迟 |
llm_time_to_first_token_ms_milliseconds | histogram | 流式响应首 Token 时间 |
llm_tokens_input_total | counter | 输入/提示词 Token 消耗 |
llm_tokens_output_total | counter | 输出/补全 Token 生成 |
llm_errors_total | counter | LLM 错误次数 |
llm_tool_calls_total | counter | 工具/函数调用次数 |
llm_tool_latency_ms_milliseconds | histogram | 工具执行延迟 |
STT 指标
| 指标名 | 类型 | 含义 |
|---|---|---|
stt_latency_ms_milliseconds | histogram | STT 处理延迟 |
stt_audio_duration_ms_milliseconds | histogram | 被处理音频的时长 |
stt_errors_total | counter | STT 错误次数 |
TTS 指标
| 指标名 | 类型 | 含义 |
|---|---|---|
tts_latency_ms_milliseconds | histogram | TTS 合成延迟 |
tts_audio_duration_ms_milliseconds | histogram | 合成音频时长 |
tts_characters_total | counter | 合成字符数 |
tts_errors_total | counter | TTS 错误次数 |
轮次检测指标(Turn Detection)
| 指标名 | 类型 | 含义 |
|---|---|---|
turn_duration_ms_milliseconds | histogram | 检测到的轮次时长 |
turn_trailing_silence_ms_milliseconds | histogram | 轮次结束前的尾部静默时长 |
Realtime LLM 指标
| 指标名 | 类型 | 含义 |
|---|---|---|
realtime_sessions_total | counter | 启动的 Realtime 会话数 |
realtime_session_duration_ms_milliseconds | histogram | 会话时长 |
realtime_audio_input_bytes_bytes | counter | 发送的音频字节数 |
realtime_audio_output_bytes_bytes | counter | 接收的音频字节数 |
realtime_responses_total | counter | 收到的响应数 |
realtime_errors_total | counter | Realtime 错误数 |
VLM 与视频处理指标(README 之外,源码已内置)
从源码看,指标定义并不止于 README 所列。核心库的 observability/metrics.py 还定义了 VLM(视觉语言模型)与视频处理器指标:
vlm_inference_latency_ms_milliseconds(histogram,VLM 推理延迟)、vlm_inferences_total(counter,推理请求数);vlm_tokens_input_total/vlm_tokens_output_total(counter,VLM 输入输出 Token);vlm_errors_total(counter);video_frames_processed_total(counter,处理的视频帧数)、video_processing_latency_ms_milliseconds(histogram,视频帧处理延迟)、video_detections_total(counter,视频中检测到的目标数)。
如果你的 Agent 接了 VLM 或视频处理器(如 05_security_camera_example 之类),这些指标同样会自动出现在/metrics中。
指标命名:从 OTel 名到 Prometheus 名的转换
metrics.py中的 OTel 指标采用点分命名、并声明单位,例如meter.create_histogram("stt.latency.ms", unit="ms")、meter.create_counter("realtime.audio.input.bytes", unit="By")。经PrometheusMetricReader导出后,名称按 Prometheus 约定转换:点号转为下划线,单位追加为后缀——于是stt.latency.ms变成stt_latency_ms_milliseconds(ms→milliseconds),realtime.audio.input.bytes变成realtime_audio_input_bytes_bytes(By→bytes)。这是观察/metrics输出与 README 指标名时值得留意的对应关系。
源码级原理:指标为何"零埋点"即可采集
Agent 自动创建并合并 MetricsCollector
在 agents/agents.py 中,Agent.__init__内部会自动完成:
self._collector = MetricsCollector() # Merge plugin metric collectors so plugin on_*() calls forward to the agent root for plugin in [stt, tts, turn_detection, llm, avatar, *self.processors]: if plugin is not None: self._collector.merge(plugin.metrics)即:每个 Agent 都有一个根MetricsCollector,所有组件(STT/TTS/TurnDetection/LLM/Avatar/Processors)各自持有的 collector 通过merge()挂到根上,构成一棵收集器树。这正是 README 所说"每个 Agent 自动创建 MetricsCollector、指标采集默认开启"的代码依据。
双通道写入:本地聚合 + OTel 输出
MetricsCollector(observability/collector.py)的设计要点:
- 每次事件(如
on_llm_response、on_stt_transcript、on_tts_synthesis、on_turn_ended等)都会先更新本地AgentMetrics聚合数据(对应 observability/agent.py 中的Counter/Average结构,可用于单次调用级别的agent.metrics访问),再向父 collector 转发; - 只有根 collector(
parent is None)才真正写入 OpenTelemetry meter,从而保证无论合并深度如何,每次事件对 OTel 只写一次("OTel is emitted exactly once per call regardless of merge depth"); - 指标记录时附带丰富属性(attribute),例如 LLM 指标携带
provider/model,工具指标携带tool_name/success,错误指标携带error_type/error_code——这些属性会成为 Prometheus 的 label 维度,便于按模型、按工具、按错误类型聚合。
未配置 Provider 时优雅降级
metrics.py的模块注释明确指出:metrics.get_meter("vision_agents.core")会使用应用配置的 provider,若应用未配置任何 provider,则所有指标操作都是 no-op。换言之,可观测性是完全可选(opt-in)的能力,不配置 OpenTelemetry 不会对 Agent 造成额外开销。这与示例"先配置 Provider,再创建 Agent"的顺序要求一致。
示例代码逐段解析
配置 OpenTelemetry → Prometheus
prometheus_metrics_example.py 中,导入各组件之前就完成了三件事:
PROMETHEUS_PORT = 9464 reader = PrometheusMetricReader() provider = MeterProvider(metric_readers=[reader]) metrics.set_meter_provider(provider)随后在if __name__ == "__main__":中调用start_http_server(PROMETHEUS_PORT)启动 9464 端口的 HTTP 服务,并最终通过Runner(AgentLauncher(create_agent=create_agent, join_call=join_call)).cli()拉起 Agent 的 CLI 入口(支持run --call-type default --call-id test-metrics之类的参数)。
README 中的核心模式节选(与原文档一致,可作为自己项目的最小模板):
from opentelemetry import metrics from opentelemetry.exporter.prometheus import PrometheusMetricReader from opentelemetry.sdk.metrics import MeterProvider from prometheus_client import start_http_server from vision_agents.core import Agent # Start Prometheus exporter on localhost:9464 start_http_server(9464) # Configure OpenTelemetry to use Prometheus reader = PrometheusMetricReader() provider = MeterProvider(metric_readers=[reader]) metrics.set_meter_provider(provider) # Setup and run the agent agent = Agent(...)构建 Agent 与注册工具
create_agent(prometheus_metrics_example.py)组装了 Gemini 的gemini-flash-lite-latest模型、ElevenLabs TTS、启用eager_turn_detection的 Deepgram STT,以及 GetStream 边缘网络连接;并通过llm.register_function注册get_weather工具——该工具调用会触发on_tool_call,进而产生llm_tool_calls_total与llm_tool_latency_ms_milliseconds指标,方便在 Grafana 上观察函数调用的频率与耗时。
加入通话并产生流量
join_call(prometheus_metrics_example.py)打印出将要采集的指标清单到 stderr,随后agent.create_call→agent.join(call)→agent.simple_response(...)建立会话并主动发起一轮问候,之后与 Agent 对话即可持续产生指标流量。
Prometheus 抓取配置详解
README 给出最小抓取配置(加入你自己的prometheus.yml):
scrape_configs: - job_name: 'stream-agents' static_configs: - targets: [ 'localhost:9464' ] scrape_interval: 15s示例仓库自带的完整配置(observability/prometheus/prometheus.yml)则做了两处关键增强:
- 全局配置:
scrape_interval: 15s、evaluation_interval: 15s,并打上外部标签monitor: 'stream-agents-monitor'; - stream-agents 抓取任务:目标为
host.docker.internal:9464(而非localhost),抓取间隔缩短到 5 秒、超时 5 秒,并为每个样本附加service: 'stream-agents'、environment: 'development'标签,便于多环境、多服务聚合;另有一个prometheusjob 抓取 Prometheus 自身指标用于自监控。
注意:README 的快速配置与仓库内配置的目标地址不同(localhost:9464vshost.docker.internal:9464),差异源于运行方式——Agent 跑在宿主机、Prometheus 跑在容器内时,容器内无法通过localhost访问宿主进程,必须使用host.docker.internal;若两者都在宿主机上运行,则用localhost即可。
Grafana 可视化:预置仪表盘与 PromQL 查询
开箱即用的容器编排
docker-compose.yml 定义了三个服务:
- prometheus(9090 端口):挂载
prometheus.yml,启用--web.enable-lifecycle以支持热重载; - grafana(3000 端口):以环境变量开启匿名访问(
GF_AUTH_ANONYMOUS_ENABLED=true、GF_AUTH_ANONYMOUS_ORG_ROLE=Admin),禁用登录表单,并将provisioning目录与dashboards目录分别挂载到容器内; - grafana-init:基于
curlimages/curl的初始化容器,执行 init-home-dashboard.sh,等待 Grafana 健康检查通过后,通过 REST API 将组织与 admin 用户的默认首页设置为stream-agents-metrics这个仪表盘。
数据源与仪表盘自动供给
Grafana 的 provisioning(免手工配置)由两处文件驱动:
- provisioning/datasources/prometheus.yml:注册名为
Prometheus的数据源,url: http://prometheus:9090(走 Docker 内部网络),uid: prometheus,设为默认数据源,并声明timeInterval: 5s与抓取节奏对齐; - provisioning/dashboards/default.yml:以文件 provider 方式加载
/var/lib/grafana/dashboards目录下的仪表盘(即挂载进去的 stream-agents.json),每 10 秒检测一次更新。
常用 PromQL 查询
README 给出了四组可直接上手的 PromQL,覆盖延迟均值、Token 速率、错误率与分位延迟:
# 平均 LLM 延迟随时间变化 rate(llm_latency_ms_milliseconds_sum[5m]) / rate(llm_latency_ms_milliseconds_count[5m]) # Token 使用速率 rate(llm_tokens_input_total[5m]) + rate(llm_tokens_output_total[5m]) # 错误率 rate(llm_errors_total[5m]) # 95 分位延迟 histogram_quantile(0.95, sum(rate(llm_latency_ms_milliseconds_bucket[5m])) by (le))前两条利用了 histogram 的_sum/_count伴生序列与 counter 的速率语义;分位查询则借助histogram_quantile对_bucket序列求 p95。将llm_latency_ms_milliseconds替换为stt_、tts_、turn_duration_、turn_trailing_silence_前缀,即可获得 README 仪表盘中列出的其他面板(p50/p95/p99)所对应的查询。
环境变量配置
示例通过load_dotenv()加载项目根目录下的.env,需要以下密钥(README 原文列出):
GOOGLE_API_KEY=your_key DEEPGRAM_API_KEY=your_key ELEVENLABS_API_KEY=your_key STREAM_API_KEY=your_key STREAM_API_SECRET=your_secret分别对应 Gemini LLM、Deepgram STT、ElevenLabs TTS 与 GetStream 边缘网络的鉴权信息。
故障排查
Prometheus 无法抓取指标
- 确保Agent 先于 Prometheus 启动,或在 Agent 启动后重启 Prometheus 容器;
- macOS 下
host.docker.internal通常开箱即用;Linux 下可能需改用--network="host"或调整目标地址(对应仓库配置中的host.docker.internal:9464说明)。
Grafana 无数据
- 等待数秒让指标完成首轮抓取;
- 到 Prometheus 的 Targets 页面(http://localhost:9090/targets)确认
stream-agentsjob 处于 UP 状态; - 确认 Agent 正在处理通话流量——指标只有在真实通话(create_call + join)期间才会产生,空跑进程不会出现 STT/TTS/LLM 序列。
文件结构索引
examples/06_prometheus_metrics_example/ ├── prometheus_metrics_example.py # 主示例代码 ├── docker-compose.yml # Prometheus + Grafana 编排 ├── observability/ │ ├── prometheus/ │ │ └── prometheus.yml # Prometheus 抓取配置 │ └── grafana/ │ ├── dashboards/ │ │ └── stream-agents.json # 预置仪表盘 │ ├── provisioning/ │ │ ├── dashboards/ │ │ │ └── default.yml # 仪表盘供给配置 │ │ └── datasources/ │ │ └── prometheus.yml # 数据源配置 │ └── init-home-dashboard.sh # 设置默认首页仪表盘 └── README.md与本文对应的核心库源码与测试位置:
- 指标定义: agents-core/vision_agents/core/observability/metrics.py
- 采集器实现: agents-core/vision_agents/core/observability/collector.py
- 本地聚合结构: agents-core/vision_agents/core/observability/agent.py
- 自动挂载逻辑: agents-core/vision_agents/core/agents/agents.py
- 相关测试: tests/test_observability.py
小结
通过 06_prometheus_metrics_example,Vision-Agents 展示了"配置即观测"的理念:应用只需在启动前配置 OpenTelemetry 的 Prometheus 导出器并启动/metricsHTTP 服务,Agent 内部自动创建的MetricsCollector就会把 LLM、STT、TTS、轮次检测、Realtime LLM(乃至 VLM 与视频处理)的延迟、Token、错误与工具调用指标持续输出;配合仓库自带的 Prometheus 抓取配置与 Grafana 供给文件,即可在几分钟内获得带 p50/p95/p99 延迟面板与错误率监控的完整可观测性栈。这套模式同样适用于把指标接入云端 Prometheus、VictoriaMetrics 等兼容后端的生产场景——只需替换导出器与抓取目标,核心 Agent 代码无需任何改动。
【免费下载链接】Vision-AgentsOpen Vision Agents by Stream. Build voice and vision agents quickly with any model or video provider. Uses Stream's edge network for ultra-low latency.项目地址: https://gitcode.com/GitHub_Trending/vi/Vision-Agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考