☰
Semantic Router 指标采集与监控指南:基于 Prometheus + Grafana 的 Metrics Monitoring 全流程实践
2026/10/13 17:13:46 网站建设 项目流程
  • 后端
  • API网关
  • 模型推理服务
  • AI Agent

【免费下载链接】semantic-router

An open, programmable decision layer for models and compute.

项目地址:https://gitcode.com/gh_mirrors/sem/semantic-router
点击查看免费下载

本篇技术指南聚焦 Semantic Router(vllm-project/semantic-router)的可观测性体系,围绕其官方教程文档 website/versioned_docs/version-v0.1/tutorials/observability/metrics.md 展开。全文以 Router 原生 Prometheus 指标为核心,覆盖本地宿主机模式、Docker Compose 模式、Kubernetes 模式三种部署形态的采集链路搭建,并深入讲解窗口化模型指标(Windowed Model Metrics)的配置、指标语义与查询方法,同时结合 src/semantic-router/pkg/observability/metrics/metrics.go 等源码级实现,帮助读者掌握从「采集端点配置 → 指标查询 → 告警规则」的完整实战能力。


1. 指标端点总览:Router 可观测性的三个入口

Semantic Router 在进程内原生暴露 Prometheus 格式指标,同时提供 HTTP 健康检查端点,若部署了 Envoy 还可采集其自身统计信息。三个端点的关系如下表:

组件端点说明
Router metrics:9190/metricsPrometheus 文本格式,端口由命令行 flag--metrics-port控制
Router health:8080/healthHTTP readiness / liveness 探针
Envoy metrics(可选):19000/stats/prometheus仅在启用 Envoy 时存在

其中 Router 的 metrics 端点并不依赖配置文件,而是由运行时启动参数直接决定。源码层面,runtime_bootstrap.go 中定义了--metrics-portflag,默认值即为9190;metricsServerEnabled()在cfg.Observability.Metrics.Enabled未显式设置时默认开启指标服务(runtime_bootstrap.go)。启动逻辑通过net.Listen("tcp", ":"+port)绑定端口,并以 metrics.NewServeMux() 作为 HTTP Handler,因此只要 Router 进程存活,curl localhost:9190/metrics即可直接拿到全部指标。

文档约定观测相关配置统一放置在tools/observability/目录(本地/Docker 模式),Kubernetes 模式则对应deploy/kubernetes/observability/,两者共享同一套 Grafana 面板定义llm-router-dashboard.json。在 v0.1 教程所处的仓库基线中,完整目录包括:

  • prometheus.yaml—— Prometheus 采集配置
  • grafana-datasource.yaml—— Grafana 数据源配置
  • grafana-dashboard.yaml—— 面板 provision 配置
  • llm-router-dashboard.json—— 面板定义(JSON)

提示:当前仓库版本已将 Kubernetes 观测栈独立成 Kustomize 包(见第 4 节),面板与告警规则也随版本演进有所扩充,但端点与核心指标语义保持一致。


2. 本地模式(Router 运行在宿主机)

本地模式适合开发调试:Router 以原生进程运行,Prometheus 与 Grafana 以 Docker 容器提供观测能力。

2.1 快速启动

# 启动 Router(宿主机进程) make run-router # 启动观测栈(Prometheus + Grafana 容器) make o11y-local

启动后的访问入口:

  • Prometheus:http://localhost:9090
  • Grafana:http://localhost:3000(默认账号admin/admin)

验证采集目标是否生效:

# 确认 Prometheus 正在抓取 localhost:9190 open http://localhost:9090/targets

停止观测栈:

make stop-observability

2.2 配置文件说明

所有配置均位于tools/observability/:

  • prometheus.yaml—— 通过环境变量ROUTER_TARGET指定抓取目标,默认值为localhost:9190
  • grafana-datasource.yaml—— Grafana 数据源指向localhost:9090
  • grafana-dashboard.yaml—— 面板自动 provisioning 配置
  • llm-router-dashboard.json—— 面板定义本身

2.3 常见故障排查

问题解决方式
Target 显示 DOWN先启动 Router:make run-router
无指标数据主动产生流量(通过 Router 发请求),并检查:9190/metrics是否有输出
端口冲突更换端口或停止占用端口的进程

值得注意:Router 的 health 端点与 metrics 端点相互独立。startMetricsServerIfEnabled()中如果端口已被占用,指标服务会记录metrics_server_failed事件并跳过启动而不会中止 Router 主流程(runtime_bootstrap.go),因此「端口冲突」通常表现为 metrics 端点不可达而非进程崩溃。


3. Docker Compose 模式:全容器化观测栈

当 Router 本身也容器化部署时,观测组件与 Router 共享同一容器网络,抓取目标从localhost变为服务名。

3.1 快速启动

# 启动完整栈(包含观测组件) docker compose -f deploy/docker-compose/docker-compose.yml up --build # 或者启用 testing profile docker compose -f deploy/docker-compose/docker-compose.yml --profile testing up --build

访问入口与本地模式一致:

  • Prometheus:http://localhost:9090
  • Grafana:http://localhost:3000(默认账号admin/admin)

预期的采集目标:

  • semantic-router:9190—— Router 指标
  • envoy-proxy:19000—— Envoy 统计信息(可选)

3.2 与本地模式的差异

配置仍复用tools/observability/下的文件,但环境变量需要指向容器服务名:

  • ROUTER_TARGET=semantic-router:9190
  • PROMETHEUS_URL=prometheus:9090
  • 使用semantic-networkbridge 网络互通

4. Kubernetes 模式:生产级观测栈

Kubernetes 模式提供面向生产环境的 Prometheus + Grafana 部署,运行在命名空间vllm-semantic-router-system中。仓库中的完整实现位于 deploy/kubernetes/observability/,由 Kustomize 组织,包含四类组件:

组件用途位置
Prometheus抓取 Router 指标,15 天保留期deploy/kubernetes/observability/prometheus/
Grafana面板可视化deploy/kubernetes/observability/grafana/
Ingress可选的外部访问入口deploy/kubernetes/observability/ingress.yaml
DashboardRouter 配置与工具数据展示deploy/kubernetes/observability/dashboard/

4.1 部署

# 渲染并应用全部 manifest kubectl apply -k deploy/kubernetes/observability/ # 验证 Pod 状态 kubectl get pods -n vllm-semantic-router-system

官方文档提醒,仓库内自带的值是「便于检查」的开发示例而非安全默认值:grafana/secret.yaml中写死admin/admin、ingress.yaml包含示例域名与 TLS Secret 名称。正式部署前应先执行kubectl kustomize deploy/kubernetes/observability/渲染检查,再通过 overlay 替换凭据、存储类与资源配额。

一个关键前提是:Router 的 metrics Service 必须存在于同一命名空间且暴露名为metrics的端口:

kubectl get service semantic-router-metrics \ --namespace vllm-semantic-router-system

4.2 访问方式

端口转发(无 Ingress 时):

kubectl port-forward svc/prometheus 9090:9090 -n vllm-semantic-router-system kubectl port-forward svc/grafana 3000:3000 -n vllm-semantic-router-system

Ingress:按需修改ingress.yaml中的域名与 TLS 配置。若通过外部代理转发内部 Host 访问 Grafana Live(WebSocket),还需在 Grafana 容器上设置GF_LIVE_ALLOWED_ORIGINS为浏览器面向 Dashboard 的真实 Origin(不要写成*),详见 deploy/kubernetes/observability/README.md。

4.3 关键配置

Prometheus 使用 Kubernetes 服务发现(Kubernetes SD):仓库中 prometheus/configmap.yaml 的实际实现比文档示例更完整——通过relabel_configs只保留服务名为semantic-router-metrics、端口名为metrics的 endpoint:

scrape_configs: - job_name: semantic-router kubernetes_sd_configs: - role: endpoints namespaces: names: [vllm-semantic-router-system] relabel_configs: - source_labels: [__meta_kubernetes_service_name] regex: semantic-router-metrics action: keep - source_labels: [__meta_kubernetes_endpoint_port_name] regex: metrics<|begin▁of▁sentence|> r action: keep

该 ConfigMap 同时设置了全局scrape_interval: 15s、evaluation_interval: 15s,并加载rule_files中的告警规则。

Grafana 数据源与面板 provisioning:grafana/configmap-provisioning.yaml 以datasources.yaml声明默认数据源http://prometheus:9090(timeInterval: 15s),以dashboards.yaml声明文件型 provider,从/var/lib/grafana-dashboards自动加载面板,面板文件由configmap-dashboard.yaml注入。

Grafana 凭据(生产环境务必替换):

kubectl create secret generic grafana-admin \ --namespace vllm-semantic-router-system \ --from-literal=admin-user=admin \ --from-literal=admin-password='your-password'

更推荐的做法是先用--dry-run=client -o yaml生成 Secret 再 apply,避免把密码写进提交的 overlay:

kubectl create secret generic grafana-admin \ --namespace vllm-semantic-router-system \ --from-literal=admin-user=monitor \ --from-literal=admin-password="$GRAFANA_ADMIN_PASSWORD" \ --dry-run=client -o yaml | kubectl apply -f - kubectl rollout restart deployment/grafana \ --namespace vllm-semantic-router-system

4.4 验证数据链路

官方 K8s 部署文档(deploy/kubernetes/observability/README.md)给出了端到端验证顺序:

  1. 打开 PrometheusStatus > Targets,确认semantic-routerjob 为 UP;
  2. 通过 Envoy 发送推理流量;
  3. 查询 Router 实际输出的指标,如llm_request_outcomes_total{traffic_kind="inference"};
  4. 打开 Grafana 已 provisioning 的 Router 面板,确认时间范围覆盖流量时段。

若 target 缺失,先检查服务发现而非面板:

kubectl get endpoints semantic-router-metrics \ --namespace vllm-semantic-router-system kubectl logs deployment/prometheus \ --namespace vllm-semantic-router-system

5. 核心指标:Router 原生导出的 Prometheus 指标

Semantic Router 的核心指标全部在 src/semantic-router/pkg/observability/metrics/metrics.go 中通过promauto.NewCounterVec/NewHistogramVec注册。教程文档列出四个基础指标:

指标类型描述
llm_category_classifications_countcounter分类(category classification)次数
llm_model_completion_tokens_totalcounter各模型的输出 token 数
llm_model_routing_modifications_totalcounter模型路由变更次数
llm_model_completion_latency_secondshistogram模型完成响应耗时

示例查询:

# 输出 token 速率(每 5 分钟) rate(llm_model_completion_tokens_total[5m]) # 完成耗时的 P95 分位数 histogram_quantile(0.95, rate(llm_model_completion_latency_seconds_bucket[5m]))

5.1 指标背后的源码实现

这组指标在源码中均有对应注册与记录函数,可作为排障时「指标为何缺失」的定位依据:

  • llm_model_completion_tokens_total:注册于 metrics.go,由RecordModelTokensDetailed(model, promptTokens, completionTokens)在累加llm_model_tokens_total与llm_model_prompt_tokens_total的同时记录(metrics.go);
  • llm_model_routing_modifications_total:注册于 metrics.go,带source_model与target_model两个 label,由RecordModelRouting(sourceModel, targetModel)在源模型与目标模型不同时才Inc()(metrics.go),因此它精确刻画了「路由决策真正改写目标模型」的次数;
  • llm_model_completion_latency_seconds:注册于 metrics.go,bucket 区间为0.005s ~ 1800s的有限序列。源码注释特别说明:由于 completion 可能包含长流式 / reasoning 响应,有限桶上界必须超出维护的 30 秒 P95 告警阈值;
  • llm_category_classifications_count:对应分类管线的计数指标。同文件还注册了llm_classification_confidence(分类置信度分布)与llm_entropy_values(Shannon 熵分布)等分类质量指标,说明分类观测并不止于次数,还包括置信度与不确定性维度(metrics.go)。

5.2 值得关注的扩展指标

除教程列出的四个基础指标外,同文件还导出了模型成本、请求错误、会话级 token 等指标,这些对生产运维同样关键:

指标类型说明
llm_model_requests_totalcounter每个模型的请求总数(label:model)
llm_request_errors_totalcounter按model与reason分类的错误计数(reason 会归一化为timeout、upstream_5xx、upstream_4xx等)
llm_model_cost_totalcounter按model与currency统计的成本
llm_model_prompt_tokens_totalcounter各模型输入 token 数
llm_prompt_tokens_per_request/llm_completion_tokens_per_requesthistogram单请求 token 数分布
llm_session_turn_prompt_tokens/llm_session_turn_completion_tokenshistogram会话级每轮 token 数(label:model、domain)
llm_model_first_response_observation_secondshistogram首个流式 chunk / 非流式响应头到达耗时
llm_model_response_duration_per_output_token_secondshistogram完整响应耗时 ÷ 输出 token 数
llm_model_routing_latency_secondshistogram路由决策本身耗时
llm_unverified_factual_responses_totalcounter缺少工具上下文无法核验的事实性响应计数

记录错误时RecordRequestError()还会把deadline_exceeded、upstream_500/502/503/504等原始值归一化为timeout、upstream_5xx,便于告警规则聚合(metrics.go)。

5.3 预置告警规则:30 秒 P95 的真实出处

教程中的histogram_quantile(0.95, ...)查询在 K8s 观测栈中被固化为告警规则。仓库 prometheus/rules.yaml 定义了HighCompletionLatencyP95等 7 条规则,其中 completion 延迟告警正是:

histogram_quantile(0.95, sum(rate(llm_model_completion_latency_seconds_bucket[5m])) by (le)) > 30 for: 10m

其余规则覆盖:请求错误率 > 5%(HighRequestErrorRate)、首响应 P95 > 5s、每输出 token 时长 P95 > 250ms、路由耗时 P95 > 100ms、单模型 in-flight 请求 > 50、缓存命中率 < 20%。这些规则均带for持续条件与dashboard_panel注解,可直接作为生产基线并路由到 Alertmanager。与之对应的契约测试位于 completion_latency_contract_test.go,用正则校验告警表达式形态,防止查询被误改。


6. 窗口化模型指标(Windowed Model Metrics)与负载均衡

窗口化指标是面向模型性能追踪的增强能力,尤其适合 Kubernetes 环境——该环境下「模型选择」与「下游 endpoint 负载均衡」是分离的职责,Router 需要输出足够细粒度的窗口指标供下游决策使用。

6.1 配置开启

在config.yaml的observability.metrics.windowed_metrics节点开启:

observability: metrics: windowed_metrics: enabled: true time_windows: ["1m", "5m", "15m", "1h", "24h"] update_interval: "10s" model_metrics: true queue_depth_estimation: true max_models: 100

字段说明:

  • enabled:总开关,关闭时RecordRequest直接忽略(windowed_metrics.go);
  • time_windows:时间窗口列表,缺省使用DefaultTimeWindows = ["1m","5m","15m","1h","24h"],每个窗口独立计算统计量;
  • update_interval:后台计算周期,默认10s;WindowedMetricsManager.Start()以该间隔驱动 ticker 调用computeWindowedMetrics()(windowed_metrics.go);
  • max_models:最多追踪的模型数,默认100;达到上限后新模型请求不再入缓冲区(windowed_metrics.go);
  • model_metrics/queue_depth_estimation:教程文档列出的配置开关。需要说明的是,当前源码的指标导出以「已观测完成的响应」为统计对象——源码注释明确WindowedMetricsManager只汇总每个模型最多 10,000 条已完成的响应,并非尝试次数、provider 失败、利用率或队列深度的完整普查(windowed_metrics.go)。

启动时InitializeWindowedMetrics()会注册全部窗口化 Gauge 并创建后台管理 goroutine;若初始化失败,Router 仅记录windowed_metrics_init_failed警告而不崩溃(runtime_bootstrap.go)。

6.2 模型级指标清单

指标类型标签描述
llm_model_latency_windowed_secondsgaugemodel, time_window窗口内平均延迟
llm_model_requests_windowed_totalgaugemodel, time_window窗口内请求数(实际为保留在窗口内的已完成响应数)
llm_model_tokens_windowed_totalgaugemodel, token_type, time_window窗口内 token 吞吐(token_type 取值prompt/completion)
llm_model_utilization_percentagegaugemodel, time_window预估利用率百分比
llm_model_queue_depth_estimatedgaugemodel当前预估队列深度
llm_model_error_rate_windowedgaugemodel, time_window窗口内错误率
llm_model_latency_p50_windowed_secondsgaugemodel, time_window窗口内 P50 延迟
llm_model_latency_p95_windowed_secondsgaugemodel, time_window窗口内 P95 延迟
llm_model_latency_p99_windowed_secondsgaugemodel, time_window窗口内 P99 延迟

注意版本差异:llm_model_utilization_percentage、llm_model_queue_depth_estimated、llm_model_error_rate_windowed三个指标出现在教程文档中,但当前仓库源码对应的 windowed_metrics_test.go 已将其标记为「退役且不再导出的指标族」(测试断言这些 family 不应存在)。若你在较新版本中查询不到这三个指标,请以llm_model_latency_*、llm_model_requests_windowed_total、llm_model_tokens_windowed_total作为负载均衡与容量规划的主要依据。

6.3 示例查询

# 某模型最近 5 分钟的平均延迟 llm_model_latency_windowed_seconds{model="gpt-4", time_window="5m"} # 跨模型对比 15 分钟窗口 P95 延迟 llm_model_latency_p95_windowed_seconds{time_window="15m"} # 各模型 1 小时窗口内的 completion token 吞吐 llm_model_tokens_windowed_total{token_type="completion", time_window="1h"} # 负载均衡决策所需的当前队列深度 llm_model_queue_depth_estimated{model="gpt-4"} # 错误率监控(5 分钟窗口错误率超过 5%) llm_model_error_rate_windowed{time_window="5m"} > 0.05

6.4 实现原理:时间窗口与百分位计算

窗口化指标的核心数据结构是每个模型一个的时间型环形缓冲区(RequestRingBuffer):

  • NewRequestRingBuffer(capacity)创建容量固定的环形数组,Add()在满时覆盖最旧数据(windowed_metrics.go);
  • 每个RequestData记录时间戳、模型、延迟、prompt/completion token 数(windowed_metrics.go);
  • 计算周期到达时,computeWindowedMetrics()对每个模型 × 每个时间窗口调用GetDataSince(now-window)过滤出窗口内数据,汇总平均延迟、token 总量,并通过computePercentile()线性插值计算 P50/P95/P99(windowed_metrics.go);
  • 空窗口会把请求数置 0、删除延迟类指标,避免把过期样本当作有效测量(这一点在 TestWindowedCompletedSamplesExpireWithoutStaleLatency 中有专门测试验证)。

这一设计意味着窗口化指标以「gauge + 前台后台周期计算」替代「counter/histogram + Prometheus 端 rate 聚合」,从而允许外部负载均衡器在不依赖 PromQL 时间范围参数的前提下,直接读取 Router 已算好的窗口统计值。

6.5 典型使用场景

  1. 负载均衡:结合队列深度与延迟指标,把请求路由到负载较轻的模型;
  2. 性能监控:跟踪各时间窗口下的 P95/P99 延迟趋势;
  3. 容量规划:观察利用率百分比,判断何时扩容模型;
  4. 告警:针对窗口内错误率或延迟尖峰设置阈值告警。

7. 汇总排查清单

问题检查项修复方式
Target DOWNPrometheus /targets 页面确认 Router 在运行且暴露:9190/metrics;K8s 下另查semantic-router-metricsService 的 endpoints 与端口名metrics
无指标流量是否经过 Router通过 Router 发送请求(分类/推理流量)后再查询
面板为空Grafana 数据源配置检查 Prometheus URL 是否可达、指标名是否拼写正确、时间范围是否覆盖流量
PVC Pending(K8s)kubectl describe pvc检查存储类是否可用
Ingress 404 / TLS 报错Ingress class、hostname、DNS按需 patch hostname 与 TLS Secret
Grafana Live 面板加载失败GF_LIVE_ALLOWED_ORIGINS在 Grafana 容器环境变量中配置浏览器面向 Dashboard 的真实 Origin

移除整个 K8s 观测示例:

kubectl delete -k deploy/kubernetes/observability/

注意:PVC 的删除与保留行为取决于集群与存储类,移除前先确认是否需要保留数据。


8. 小结

Semantic Router 的观测体系是一套「Router 原生指标 + 三种部署形态 + 窗口化模型指标」的组合:--metrics-port控制的:9190/metrics是数据源头;本地与 Docker Compose 模式通过tools/observability/下的配置快速拉起 Prometheus/Grafana;Kubernetes 模式以deploy/kubernetes/observability/Kustomize 包提供生产级服务发现、15 天保留期与 7 条预置告警规则;窗口化指标则在 Router 进程内按时间窗口预先计算平均/分位延迟与 token 吞吐,供外部负载均衡与容量规划直接消费。配合 metrics.go 与 windowed_metrics.go 的源码实现,读者可以精确掌握每条指标的触发条件、标签语义与统计口径,进而搭建出贴合自身业务的可观测与告警体系。

  • 后端
  • API网关
  • 模型推理服务
  • AI Agent

【免费下载链接】semantic-router

An open, programmable decision layer for models and compute.

项目地址:https://gitcode.com/gh_mirrors/sem/semantic-router
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询