通义千问3-Reranker-0.6B代码实例:Prometheus指标埋点+Grafana看板

你是不是也遇到过这样的问题?部署了一个AI模型服务,比如文本重排序模型,用起来感觉不错,但心里总是没底——现在到底有多少人在用?响应速度怎么样?有没有出错?模型推理的准确率如何?

这些问题就像开车没有仪表盘,只能凭感觉。今天我就来分享一个实用的解决方案:给通义千问3-Reranker-0.6B模型服务加上完整的监控系统。通过Prometheus采集指标,用Grafana可视化展示,让你对模型服务的运行状态一目了然。

1. 为什么需要监控AI模型服务?

在介绍具体实现之前,我们先聊聊为什么监控这么重要。

1.1 模型服务的特殊性

AI模型服务跟传统的Web服务不太一样。它有几个特点:

  • 资源消耗大:特别是GPU内存和显存,很容易成为瓶颈
  • 响应时间波动:不同长度的输入文本,处理时间差异很大
  • 准确率需要监控:模型效果不是100%稳定,需要持续关注
  • 并发能力有限:GPU的并行处理能力有上限

1.2 监控能帮你解决什么问题?

有了监控系统,你就能:

  1. 实时了解服务状态:当前有多少请求在处理?响应时间是多少?
  2. 快速定位问题:服务变慢了?是GPU内存不够还是CPU瓶颈?
  3. 优化资源配置:根据实际使用情况调整服务器配置
  4. 评估模型效果:监控重排序的准确率和相关性分数分布
  5. 制定扩容计划:知道什么时候需要增加服务器

2. 整体架构设计

我们的监控系统采用经典的Prometheus + Grafana组合,下面是整体架构:

用户请求 → Qwen3-Reranker服务 → 指标埋点 → Prometheus采集 → Grafana展示

2.1 技术栈选择

  • Prometheus:开源的监控系统,专门用于收集和存储时间序列数据
  • Grafana:数据可视化平台,可以创建漂亮的监控仪表板
  • Python Prometheus客户端:在模型服务中埋点,暴露指标

2.2 监控指标设计

我们需要监控以下几类指标:

指标类别 具体指标 说明
服务性能 请求总数、请求速率、响应时间 了解服务负载和性能
资源使用 GPU内存使用率、GPU利用率、CPU使用率 监控硬件资源瓶颈
模型质量 相关性分数分布、平均分数、高分比例 评估模型效果
错误监控 错误请求数、错误率 及时发现服务问题

3. 代码实现:在Qwen3-Reranker中埋点

现在进入实战部分。我们要在原有的Qwen3-Reranker服务代码中加入Prometheus指标采集。

3.1 安装依赖

首先,在服务环境中安装必要的Python包:

pip install prometheus-client

3.2 创建指标定义文件

创建一个新的Python文件 metrics.py,专门用来定义和初始化所有监控指标:

# metrics.py
from prometheus_client import Counter, Histogram, Gauge, Summary, start_http_server
import time

class RerankerMetrics:
    """Qwen3-Reranker监控指标类"""
    
    def __init__(self):
        # 请求相关指标
        self.request_total = Counter(
            'reranker_requests_total',
            'Total number of requests',
            ['method', 'endpoint']
        )
        
        self.request_duration = Histogram(
            'reranker_request_duration_seconds',
            'Request duration in seconds',
            ['method', 'endpoint'],
            buckets=[0.1, 0.5, 1.0, 2.0, 5.0, 10.0]
        )
        
        # 模型推理指标
        self.inference_time = Histogram(
            'reranker_inference_duration_seconds',
            'Model inference duration in seconds',
            buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 2.0]
        )
        
        self.batch_size = Histogram(
            'reranker_batch_size',
            'Number of documents per request',
            buckets=[1, 5, 10, 20, 50, 100]
        )
        
        # 相关性分数指标
        self.score_distribution = Histogram(
            'reranker_score_distribution',
            'Distribution of relevance scores',
            buckets=[0.1, 0.3, 0.5, 0.7, 0.9, 1.0]
        )
        
        self.high_score_count = Counter(
            'reranker_high_scores_total',
            'Number of high relevance scores (>=0.8)'
        )
        
        # 资源使用指标
        self.gpu_memory_usage = Gauge(
            'reranker_gpu_memory_usage_bytes',
            'GPU memory usage in bytes'
        )
        
        self.gpu_utilization = Gauge(
            'reranker_gpu_utilization_percent',
            'GPU utilization percentage'
        )
        
        # 错误指标
        self.error_total = Counter(
            'reranker_errors_total',
            'Total number of errors',
            ['error_type']
        )
        
        # 启动Prometheus HTTP服务器
        start_http_server(8000)
        print("Prometheus metrics server started on port 8000")
    
    def record_request(self, method, endpoint, duration):
        """记录请求信息"""
        self.request_total.labels(method=method, endpoint=endpoint).inc()
        self.request_duration.labels(method=method, endpoint=endpoint).observe(duration)
    
    def record_inference(self, duration, batch_size):
        """记录推理信息"""
        self.inference_time.observe(duration)
        self.batch_size.observe(batch_size)
    
    def record_score(self, score):
        """记录相关性分数"""
        self.score_distribution.observe(score)
        if score >= 0.8:
            self.high_score_count.inc()
    
    def record_error(self, error_type):
        """记录错误信息"""
        self.error_total.labels(error_type=error_type).inc()
    
    def update_gpu_metrics(self, memory_used, memory_total, utilization):
        """更新GPU指标"""
        self.gpu_memory_usage.set(memory_used)
        if memory_total > 0:
            memory_percent = (memory_used / memory_total) * 100
            # 这里可以添加内存使用率指标
        self.gpu_utilization.set(utilization)

# 创建全局指标实例
metrics = RerankerMetrics()

3.3 修改主服务代码

接下来,修改原有的Qwen3-Reranker服务代码,加入指标采集。这里以Gradio Web界面为例:

# app_with_metrics.py
import gradio as gr
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
import time
from metrics import metrics
import psutil
import pynvml  # 用于获取GPU信息

# 初始化GPU监控
try:
    pynvml.nvmlInit()
    has_gpu = True
except:
    has_gpu = False
    print("GPU monitoring not available")

class Qwen3RerankerWithMetrics:
    """带监控的Qwen3-Reranker服务"""
    
    def __init__(self, model_path):
        print("Loading model...")
        start_time = time.time()
        
        # 加载tokenizer和模型
        self.tokenizer = AutoTokenizer.from_pretrained(
            model_path, 
            padding_side='left',
            trust_remote_code=True
        )
        
        self.model = AutoModelForCausalLM.from_pretrained(
            model_path,
            torch_dtype=torch.float16,
            device_map="auto",
            trust_remote_code=True
        ).eval()
        
        load_time = time.time() - start_time
        print(f"Model loaded in {load_time:.2f} seconds")
        
        # 记录模型加载时间
        metrics.model_load_time = Gauge(
            'reranker_model_load_time_seconds',
            'Model loading time in seconds'
        )
        metrics.model_load_time.set(load_time)
    
    def get_gpu_info(self):
        """获取GPU信息"""
        if not has_gpu:
            return 0, 0, 0
        
        try:
            handle = pynvml.nvmlDeviceGetHandleByIndex(0)
            memory_info = pynvml.nvmlDeviceGetMemoryInfo(handle)
            utilization = pynvml.nvmlDeviceGetUtilizationRates(handle)
            
            return memory_info.used, memory_info.total, utilization.gpu
        except:
            return 0, 0, 0
    
    def rerank(self, query, documents, instruction=None):
        """重排序主函数,带监控"""
        request_start = time.time()
        
        try:
            # 更新GPU指标
            gpu_used, gpu_total, gpu_util = self.get_gpu_info()
            metrics.update_gpu_metrics(gpu_used, gpu_total, gpu_util)
            
            # 解析输入
            docs = [d.strip() for d in documents.strip().split('\n') if d.strip()]
            if not docs:
                return "请输入至少一个文档"
            
            batch_size = len(docs)
            
            # 构建指令
            if not instruction:
                instruction = "Given a query, retrieve relevant passages"
            
            # 为每个文档计算分数
            results = []
            inference_total_time = 0
            
            for doc in docs:
                # 构建输入文本
                text = f"<Instruct>: {instruction}\n<Query>: {query}\n<Document>: {doc}"
                
                # 推理
                inference_start = time.time()
                inputs = self.tokenizer(text, return_tensors="pt").to(self.model.device)
                
                with torch.no_grad():
                    logits = self.model(**inputs).logits[:, -1, :]
                    # 获取yes/no的logits
                    no_id = self.tokenizer.convert_tokens_to_ids("no")
                    yes_id = self.tokenizer.convert_tokens_to_ids("yes")
                    
                    if no_id is not None and yes_id is not None:
                        score = torch.softmax(
                            logits[:, [no_id, yes_id]], 
                            dim=1
                        )[:, 1].item()
                    else:
                        # 备用方案:使用其他方式计算分数
                        score = 0.5
                
                inference_time = time.time() - inference_start
                inference_total_time += inference_time
                
                # 记录推理指标
                metrics.record_inference(inference_time, 1)
                metrics.record_score(score)
                
                results.append({
                    'document': doc,
                    'score': score,
                    'inference_time': inference_time
                })
            
            # 按分数排序
            sorted_results = sorted(results, key=lambda x: x['score'], reverse=True)
            
            # 构建输出
            output = "排序结果(按相关性从高到低):\n\n"
            for i, result in enumerate(sorted_results, 1):
                output += f"{i}. 分数: {result['score']:.4f}\n"
                output += f"   文档: {result['document'][:100]}...\n"
                output += f"   推理时间: {result['inference_time']:.3f}秒\n\n"
            
            # 记录请求指标
            request_duration = time.time() - request_start
            metrics.record_request('POST', '/rerank', request_duration)
            metrics.record_inference(inference_total_time, batch_size)
            
            # 添加统计信息
            avg_score = sum(r['score'] for r in results) / len(results)
            output += f"\n统计信息:\n"
            output += f"- 平均相关性分数: {avg_score:.4f}\n"
            output += f"- 总推理时间: {inference_total_time:.3f}秒\n"
            output += f"- 平均每文档: {inference_total_time/len(results):.3f}秒\n"
            output += f"- 请求总耗时: {request_duration:.3f}秒"
            
            return output
            
        except Exception as e:
            # 记录错误
            error_type = type(e).__name__
            metrics.record_error(error_type)
            
            return f"处理出错: {str(e)}"

def create_web_interface():
    """创建Gradio Web界面"""
    # 初始化模型
    MODEL_PATH = "/opt/qwen3-reranker/model/Qwen3-Reranker-0.6B"
    reranker = Qwen3RerankerWithMetrics(MODEL_PATH)
    
    # 定义界面
    with gr.Blocks(title="Qwen3-Reranker with Monitoring") as demo:
        gr.Markdown("# 🎯 Qwen3-Reranker 文本重排序服务")
        gr.Markdown("### 带Prometheus监控的增强版本")
        
        with gr.Row():
            with gr.Column(scale=2):
                query = gr.Textbox(
                    label="查询语句",
                    placeholder="请输入您要查询的问题或关键词...",
                    lines=2
                )
                
                documents = gr.Textbox(
                    label="候选文档(每行一个)",
                    placeholder="请输入候选文档,每行一个...",
                    lines=10
                )
                
                instruction = gr.Textbox(
                    label="自定义指令(可选)",
                    placeholder="例如:Given a query, retrieve relevant passages",
                    value="Given a query, retrieve relevant passages"
                )
                
                submit_btn = gr.Button("开始排序", variant="primary")
                
            with gr.Column(scale=3):
                output = gr.Textbox(
                    label="排序结果",
                    lines=20,
                    interactive=False
                )
        
        # 监控信息展示
        with gr.Accordion("📊 实时监控信息", open=False):
            with gr.Row():
                metrics_url = gr.Markdown(
                    f"### Prometheus指标地址: http://localhost:8000/metrics"
                )
            
            with gr.Row():
                gr.Markdown("""
                **监控指标说明:**
                - `reranker_requests_total`: 总请求数
                - `reranker_request_duration_seconds`: 请求耗时分布
                - `reranker_inference_duration_seconds`: 模型推理耗时
                - `reranker_score_distribution`: 相关性分数分布
                - `reranker_errors_total`: 错误统计
                """)
        
        # 示例
        with gr.Accordion("📝 示例", open=False):
            gr.Examples(
                examples=[
                    [
                        "什么是机器学习?",
                        "机器学习是人工智能的一个分支\n深度学习是机器学习的一种方法\n统计学是数据分析的基础",
                        "Given a query, retrieve relevant passages"
                    ],
                    [
                        "如何学习Python编程?",
                        "Python是一种高级编程语言\n建议从基础语法开始学习\n多写代码实践很重要",
                        "Given a query, retrieve relevant passages"
                    ]
                ],
                inputs=[query, documents, instruction]
            )
        
        # 绑定事件
        submit_btn.click(
            fn=reranker.rerank,
            inputs=[query, documents, instruction],
            outputs=output
        )
    
    return demo

if __name__ == "__main__":
    print("启动带监控的Qwen3-Reranker服务...")
    print("Prometheus指标地址: http://localhost:8000/metrics")
    print("Gradio Web界面地址: http://localhost:7860")
    
    demo = create_web_interface()
    demo.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False
    )

3.4 创建Prometheus配置文件

为了让Prometheus能够采集我们的指标,需要创建一个配置文件 prometheus.yml

# prometheus.yml
global:
  scrape_interval: 15s  # 每15秒采集一次
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'qwen3-reranker'
    static_configs:
      - targets: ['localhost:8000']  # 我们的指标服务地址
    metrics_path: '/metrics'
    scrape_interval: 10s  # 对这个job可以设置更频繁的采集

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['localhost:9100']  # 节点监控

  - job_name: 'cadvisor'
    static_configs:
      - targets: ['localhost:8080']  # 容器监控(如果使用容器)

3.5 创建Docker部署文件

如果你使用Docker部署,可以创建以下Dockerfile和docker-compose文件:

# Dockerfile
FROM pytorch/pytorch:2.1.0-cuda11.8-cudnn8-runtime

WORKDIR /app

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    curl \
    wget \
    git \
    && rm -rf /var/lib/apt/lists/*

# 安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 安装Prometheus和Grafana
RUN wget https://github.com/prometheus/prometheus/releases/download/v2.45.0/prometheus-2.45.0.linux-amd64.tar.gz && \
    tar xvfz prometheus-*.tar.gz && \
    mv prometheus-2.45.0.linux-amd64 /opt/prometheus && \
    rm prometheus-*.tar.gz

RUN wget https://dl.grafana.com/oss/release/grafana-10.0.3.linux-amd64.tar.gz && \
    tar -zxvf grafana-10.0.3.linux-amd64.tar.gz && \
    mv grafana-10.0.3 /opt/grafana && \
    rm grafana-10.0.3.linux-amd64.tar.gz

# 复制代码和配置文件
COPY . .

# 下载模型(如果模型不在镜像中)
# RUN python download_model.py

# 暴露端口
EXPOSE 7860  # Gradio Web界面
EXPOSE 8000  # Prometheus指标
EXPOSE 3000  # Grafana
EXPOSE 9090  # Prometheus UI

# 启动脚本
COPY start.sh /start.sh
RUN chmod +x /start.sh

CMD ["/start.sh"]
# docker-compose.yml
version: '3.8'

services:
  qwen3-reranker:
    build: .
    container_name: qwen3-reranker
    ports:
      - "7860:7860"  # Gradio Web界面
      - "8000:8000"  # Prometheus指标端点
    volumes:
      - ./models:/app/models
      - ./data:/app/data
    environment:
      - MODEL_PATH=/app/models/Qwen3-Reranker-0.6B
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    command: python app_with_metrics.py

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--storage.tsdb.retention.time=200h'
      - '--web.enable-lifecycle'
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_USERS_ALLOW_SIGN_UP=false
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.rootfs=/rootfs'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    restart: unless-stopped

volumes:
  prometheus_data:
  grafana_data:
# start.sh
#!/bin/bash

# 启动Prometheus指标服务(在后台)
python -c "from metrics import metrics; print('Metrics server ready')" &

# 启动主服务
python app_with_metrics.py

4. 配置Grafana监控看板

现在服务已经可以暴露指标了,接下来我们配置Grafana来可视化这些指标。

4.1 添加Prometheus数据源

  1. 访问Grafana(通常是 http://localhost:3000)
  2. 使用默认账号密码登录(admin/admin)
  3. 进入 Configuration → Data Sources
  4. 点击 Add data source,选择 Prometheus
  5. 配置URL为 http://prometheus:9090(如果在同一Docker网络)或 http://localhost:9090
  6. 点击 Save & Test

4.2 创建监控看板

我们可以创建一个完整的监控看板,包含多个面板。这里提供一个完整的JSON配置,你可以直接导入到Grafana中:

{
  "dashboard": {
    "title": "Qwen3-Reranker监控看板",
    "description": "通义千问3重排序模型服务监控",
    "tags": ["qwen3", "reranker", "ai", "monitoring"],
    "style": "dark",
    "timezone": "browser",
    "panels": [
      {
        "id": 1,
        "title": "请求统计",
        "type": "stat",
        "gridPos": {"h": 4, "w": 6, "x": 0, "y": 0},
        "targets": [{
          "expr": "rate(reranker_requests_total[5m])",
          "legendFormat": "请求速率",
          "refId": "A"
        }],
        "fieldConfig": {
          "defaults": {
            "unit": "req/s",
            "color": {"mode": "thresholds"},
            "thresholds": {
              "steps": [
                {"color": "green", "value": null},
                {"color": "red", "value": 80}
              ]
            }
          }
        }
      },
      {
        "id": 2,
        "title": "平均响应时间",
        "type": "stat",
        "gridPos": {"h": 4, "w": 6, "x": 6, "y": 0},
        "targets": [{
          "expr": "rate(reranker_request_duration_seconds_sum[5m]) / rate(reranker_request_duration_seconds_count[5m])",
          "legendFormat": "平均响应时间",
          "refId": "A"
        }],
        "fieldConfig": {
          "defaults": {
            "unit": "s",
            "decimals": 3,
            "color": {"mode": "thresholds"},
            "thresholds": {
              "steps": [
                {"color": "green", "value": null},
                {"color": "yellow", "value": 1},
                {"color": "red", "value": 3}
              ]
            }
          }
        }
      },
      {
        "id": 3,
        "title": "错误率",
        "type": "stat",
        "gridPos": {"h": 4, "w": 6, "x": 12, "y": 0},
        "targets": [{
          "expr": "rate(reranker_errors_total[5m]) / rate(reranker_requests_total[5m])",
          "legendFormat": "错误率",
          "refId": "A"
        }],
        "fieldConfig": {
          "defaults": {
            "unit": "percentunit",
            "decimals": 2,
            "color": {"mode": "thresholds"},
            "thresholds": {
              "steps": [
                {"color": "green", "value": null},
                {"color": "yellow", "value": 0.01},
                {"color": "red", "value": 0.05}
              ]
            }
          }
        }
      },
      {
        "id": 4,
        "title": "GPU内存使用",
        "type": "gauge",
        "gridPos": {"h": 4, "w": 6, "x": 18, "y": 0},
        "targets": [{
          "expr": "reranker_gpu_memory_usage_bytes",
          "legendFormat": "GPU内存",
          "refId": "A"
        }],
        "fieldConfig": {
          "defaults": {
            "unit": "bytes",
            "min": 0,
            "max": 16000000000,
            "thresholds": {
              "steps": [
                {"color": "green", "value": null},
                {"color": "yellow", "value": 12000000000},
                {"color": "red", "value": 14000000000}
              ]
            }
          }
        }
      },
      {
        "id": 5,
        "title": "请求耗时分布",
        "type": "heatmap",
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 4},
        "targets": [{
          "expr": "histogram_quantile(0.95, sum(rate(reranker_request_duration_seconds_bucket[5m])) by (le))",
          "legendFormat": "P95响应时间",
          "refId": "A"
        }],
        "fieldConfig": {
          "defaults": {
            "unit": "s",
            "color": {"mode": "scheme", "schemeName": "Oranges"}
          }
        }
      },
      {
        "id": 6,
        "title": "相关性分数分布",
        "type": "histogram",
        "gridPos": {"h": 8, "w": 12, "x": 12, "y": 4},
        "targets": [{
          "expr": "reranker_score_distribution",
          "legendFormat": "分数分布",
          "refId": "A"
        }],
        "fieldConfig": {
          "defaults": {
            "color": {"mode": "palette-classic"}
          }
        }
      },
      {
        "id": 7,
        "title": "请求趋势",
        "type": "timeseries",
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 12},
        "targets": [
          {
            "expr": "rate(reranker_requests_total[5m])",
            "legendFormat": "请求速率",
            "refId": "A"
          },
          {
            "expr": "reranker_requests_total",
            "legendFormat": "总请求数",
            "refId": "B"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "req/s"
          }
        }
      },
      {
        "id": 8,
        "title": "模型推理时间",
        "type": "timeseries",
        "gridPos": {"h": 8, "w": 12, "x": 12, "y": 12},
        "targets": [{
          "expr": "rate(reranker_inference_duration_seconds_sum[5m]) / rate(reranker_inference_duration_seconds_count[5m])",
          "legendFormat": "平均推理时间",
          "refId": "A"
        }],
        "fieldConfig": {
          "defaults": {
            "unit": "s",
            "decimals": 3
          }
        }
      },
      {
        "id": 9,
        "title": "错误类型分布",
        "type": "barchart",
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 20},
        "targets": [{
          "expr": "sum by (error_type) (rate(reranker_errors_total[5m]))",
          "legendFormat": "{{error_type}}",
          "refId": "A"
        }],
        "fieldConfig": {
          "defaults": {
            "color": {"mode": "palette-classic"},
            "custom": {
              "axisPlacement": "auto",
              "barAlignment": 0,
              "drawStyle": "line",
              "fillOpacity": 10,
              "gradientMode": "none",
              "lineInterpolation": "linear",
              "lineWidth": 1,
              "pointSize": 5,
              "showPoints": "auto",
              "spanNulls": false,
              "hideFrom": {
                "legend": false,
                "tooltip": false,
                "viz": false
              }
            }
          }
        }
      },
      {
        "id": 10,
        "title": "高分比例(>=0.8)",
        "type": "gauge",
        "gridPos": {"h": 8, "w": 12, "x": 12, "y": 20},
        "targets": [{
          "expr": "rate(reranker_high_scores_total[5m]) / rate(reranker_score_distribution_count[5m])",
          "legendFormat": "高分比例",
          "refId": "A"
        }],
        "fieldConfig": {
          "defaults": {
            "unit": "percentunit",
            "min": 0,
            "max": 1,
            "thresholds": {
              "steps": [
                {"color": "red", "value": null},
                {"color": "yellow", "value": 0.3},
                {"color": "green", "value": 0.6}
              ]
            }
          }
        }
      }
    ],
    "time": {
      "from": "now-6h",
      "to": "now"
    },
    "timepicker": {
      "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"],
      "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
    }
  },
  "overwrite": true
}

4.3 导入看板到Grafana

  1. 在Grafana中,点击左侧的"+"号,选择"Import"
  2. 将上面的JSON内容粘贴到"Import via panel json"框中
  3. 点击"Load"
  4. 选择Prometheus数据源
  5. 点击"Import"

现在你就有了一个完整的Qwen3-Reranker监控看板!

5. 监控系统使用指南

5.1 访问监控系统

系统启动后,你可以通过以下地址访问:

服务 地址 用途
Qwen3-Reranker Web界面 http://localhost:7860 模型服务界面
Prometheus指标端点 http://localhost:8000/metrics 查看原始指标数据
Prometheus UI http://localhost:9090 Prometheus管理界面
Grafana看板 http://localhost:3000 可视化监控看板

5.2 关键指标解读

5.2.1 服务健康指标
  • 请求速率:正常情况应该稳定,突然下降可能表示服务有问题
  • 错误率:应该接近0%,超过1%需要关注
  • 响应时间P95:95%的请求在这个时间内完成,是衡量服务性能的关键指标
5.2.2 模型质量指标
  • 相关性分数分布:大部分分数应该集中在0.5-1.0之间
  • 高分比例:分数≥0.8的比例,越高说明模型效果越好
  • 平均推理时间:反映模型处理速度,突然变慢可能有问题
5.2.3 资源使用指标
  • GPU内存使用:接近显存上限时需要关注
  • GPU利用率:理想情况应该较高,表示GPU被充分利用

5.3 告警配置

你还可以在Grafana中配置告警,当指标异常时自动通知:

  1. 在Grafana看板中,点击任意面板标题,选择"Edit"
  2. 切换到"Alert"标签页
  3. 配置告警规则,例如:
    • 当错误率 > 5% 持续5分钟时告警
    • 当平均响应时间 > 3秒 持续5分钟时告警
    • 当GPU内存使用 > 90% 时告警
  4. 配置通知渠道(邮件、Slack、钉钉等)

6. 高级功能扩展

6.1 自定义业务指标

除了基础监控,你还可以添加业务特定的指标:

# 在metrics.py中添加
class BusinessMetrics:
    """业务特定指标"""
    
    def __init__(self):
        # 查询类型分布
        self.query_type = Counter(
            'reranker_query_type_total',
            'Query type distribution',
            ['query_type']
        )
        
        # 文档长度分布
        self.doc_length = Histogram(
            'reranker_document_length_chars',
            'Document length in characters',
            buckets=[100, 500, 1000, 2000, 5000]
        )
        
        # 用户行为分析
        self.user_sessions = Counter(
            'reranker_user_sessions_total',
            'Number of user sessions'
        )
    
    def record_query_type(self, query_text):
        """分析查询类型"""
        query_type = self.analyze_query_type(query_text)
        self.query_type.labels(query_type=query_type).inc()
    
    def analyze_query_type(self, text):
        """简单的查询类型分析"""
        text_lower = text.lower()
        if any(word in text_lower for word in ['什么', '是什么', '定义']):
            return 'definition'
        elif any(word in text_lower for word in ['如何', '怎么', '方法']):
            return 'howto'
        elif any(word in text_lower for word in ['为什么', '原因']):
            return 'why'
        else:
            return 'general'

6.2 性能优化建议

基于监控数据,你可以做以下优化:

  1. 批处理优化:如果单个请求的文档很多,可以考虑批处理
  2. 缓存策略:对相同的查询结果进行缓存
  3. 模型量化:使用INT8量化减少内存占用
  4. 动态批处理:根据GPU使用情况动态调整批处理大小

6.3 长期数据存储

对于长期监控,建议将数据存储到时序数据库中:

# 使用VictoriaMetrics长期存储
# 修改prometheus.yml
remote_write:
  - url: http://victoriametrics:8428/api/v1/write

7. 总结

通过给Qwen3-Reranker-0.6B模型服务添加Prometheus指标埋点和Grafana监控看板,我们实现了:

  1. 全方位监控:从服务性能、资源使用到模型质量的全方位监控
  2. 实时可视化:通过Grafana看板实时查看服务状态
  3. 问题预警:配置告警规则,及时发现和处理问题
  4. 数据驱动优化:基于监控数据优化服务配置和模型使用

这个监控方案不仅适用于Qwen3-Reranker,也可以轻松适配其他AI模型服务。关键是要设计好监控指标,既要覆盖全面,又要避免指标过多导致维护困难。

7.1 主要收获

  • 服务可观测性:不再是"黑盒",而是可以清晰看到内部状态
  • 问题快速定位:通过监控指标快速定位性能瓶颈
  • 容量规划依据:基于历史数据做出合理的扩容决策
  • 模型效果评估:持续监控模型在实际使用中的表现

7.2 下一步建议

  1. 添加更多业务指标:根据实际业务需求添加特定指标
  2. 设置自动化告警:配置邮件、钉钉等告警通知
  3. 建立监控规范:为团队其他服务建立统一的监控标准
  4. 性能基准测试:定期进行性能测试,建立性能基线

监控不是一次性的工作,而是一个持续的过程。随着业务发展,监控系统也需要不断优化和调整。希望这个方案能帮助你更好地管理和优化AI模型服务。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐