{
  "annotations": { "list": [] },
  "title": "flag92 — AI Customer Support KPI Dashboard",
  "description": "AI 客服核心 KPI 仪表盘模板 (Grafana 10+)",
  "tags": ["ai-support", "kpi", "chatwoot", "dify"],
  "timezone": "browser",
  "refresh": "1m",
  "schemaVersion": 39,
  "version": 1,
  "panels": [
    {
      "title": "AI 自助解决率 (24h)",
      "type": "stat",
      "gridPos": { "h": 6, "w": 6, "x": 0, "y": 0 },
      "targets": [
        {
          "rawSql": "SELECT (count(*) FILTER (WHERE ai_handled AND NOT human_handoff))::float / NULLIF(count(*), 0) AS rate FROM conversations WHERE created_at > now() - interval '24 hours'",
          "format": "time_series"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "percentunit",
          "thresholds": { "mode": "absolute", "steps": [
            { "color": "red", "value": null },
            { "color": "yellow", "value": 0.40 },
            { "color": "green", "value": 0.60 }
          ]}
        }
      }
    },
    {
      "title": "首响时间 P50 / P95",
      "type": "timeseries",
      "gridPos": { "h": 8, "w": 12, "x": 6, "y": 0 },
      "targets": [
        { "rawSql": "SELECT date_trunc('5 minutes', created_at) AS time, percentile_cont(0.5) WITHIN GROUP (ORDER BY first_response_ms) AS p50, percentile_cont(0.95) WITHIN GROUP (ORDER BY first_response_ms) AS p95 FROM conversations WHERE created_at > now() - interval '6 hours' GROUP BY 1 ORDER BY 1" }
      ]
    },
    {
      "title": "CSAT (AI vs 人工)",
      "type": "bargauge",
      "gridPos": { "h": 6, "w": 6, "x": 18, "y": 0 },
      "targets": [
        { "rawSql": "SELECT 'AI' AS metric, avg(rating) FROM csat WHERE leg = 'ai' AND created_at > now() - interval '7 days' UNION ALL SELECT 'Human', avg(rating) FROM csat WHERE leg = 'human' AND created_at > now() - interval '7 days'" }
      ]
    },
    {
      "title": "LLM tokens / 小时",
      "type": "timeseries",
      "gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 },
      "targets": [
        { "rawSql": "SELECT date_trunc('hour', t) AS time, sum(prompt_tokens) AS prompt, sum(completion_tokens) AS completion FROM llm_calls WHERE t > now() - interval '24 hours' GROUP BY 1 ORDER BY 1" }
      ]
    },
    {
      "title": "工单数 / 渠道",
      "type": "piechart",
      "gridPos": { "h": 8, "w": 6, "x": 12, "y": 8 },
      "targets": [
        { "rawSql": "SELECT channel_type, count(*) FROM conversations WHERE created_at > now() - interval '7 days' GROUP BY channel_type" }
      ]
    },
    {
      "title": "知识库命中率",
      "type": "stat",
      "gridPos": { "h": 8, "w": 6, "x": 18, "y": 8 },
      "targets": [
        { "rawSql": "SELECT count(*) FILTER (WHERE top1_score > 0.5)::float / NULLIF(count(*), 0) FROM rag_queries WHERE created_at > now() - interval '24 hours'" }
      ],
      "fieldConfig": { "defaults": { "unit": "percentunit" } }
    }
  ]
}
