基于大模型的诊断自动化——从告警触发到根因分析的端到端排障 Agent 设计
·
基于大模型的诊断自动化——从告警触发到根因分析的端到端排障 Agent 设计
一、"凌晨 3 点的告警,需要 5 分钟定位还是 50 分钟"
一条典型的 P1 告警:VLLMHighTTFT: P99 TTFT 850ms (阈值 500ms, 节点 gpu-node-03)。
守夜的工程师收到告警后的排查路径:
- 打开 Grafana,查看 node-03 的 GPU 利用率、显存、QPS 趋势(3 分钟)。
- 对比同集群其他节点的指标,确认是否单点问题(2 分钟)。
- 查看 node-03 的 vLLM 日志,搜索
ERROR和WARN关键字(5 分钟)。 - 发现
preemption triggered: no available blocks日志,确认是 KV Cache 饱和(3 分钟)。 - 查看该节点的 KV Cache 使用率趋势,确认触发时间(2 分钟)。
- 检查是否有异常的长 prompt 请求(3 分钟)。
- 在 Grafana 上找到触发时刻的请求日志,确认是一个 12000 token 的超长 prompt(5 分钟)。
- 定位到具体请求来源(业务方 A),通知处理(5 分钟)。
总耗时约 28 分钟。其中真正需要人类决策的只有第 8 步("谁的问题?通知谁?"),前 7 步都是"查这个 → 看那个 → 对比这个"的机械操作。
如果有一个排障 Agent 自动执行前 7 步,工程师在第 1 分钟就能拿到结论:"node-03 的 KV Cache 被一个 12000 token 的超长 prompt 填满,来源是业务 A 的批量处理任务。建议通知业务 A 拆分批处理,或为该节点增加 reserved blocks 配置。"
二、排障 Agent 的架构设计
flowchart TD
A[告警触发<br/>Prometheus Alert] --> B[Agent 接收告警]
B --> C[任务规划 LLM<br/>将告警转化为排查步骤]
C --> D{第 1 步: 多维指标查询}
D --> E[Grafana API<br/>GPU利用率/显存/QPS]
E --> F{指标对比分析}
F -->|单点异常| G1[第 2 步: 日志检索]
F -->|全集群异常| G2[第 2 步: 关联变更记录]
G1 --> H[Loki/ES 日志查询<br/>error 关键字 + 时间范围]
H --> I{日志语义分析}
I --> J[第 3 步: 根因推断 LLM<br/>综合分析 → 根因假设]
J --> K{验证假设}
K -->|需要验证| L[第 4 步: 补充查询<br/>如: KV Cache 趋势/请求日志]
L --> J
K -->|确认根因| M[第 5 步: 生成诊断报告]
M --> N{是否需要人工介入}
N -->|是| O[推送到值班群<br/>含诊断摘要 + 建议方案]
N -->|否| P[自动执行修复<br/>如: 扩容/限流/重启]
2.1 Agent 的核心能力
| 能力 | 工具 | 示例 |
|---|---|---|
| 指标查询 | Prometheus API | query_range(vllm:gpu_cache_usage_perc, 30m) |
| 日志检索 | Loki / ElasticSearch | `{job="vllm"} |
| 代码/配置变更 | Git API | 查看最近合并的 PR |
| 系统命令执行 | SSH / kubectl exec | nvidia-smi, dmesg, free -h |
| 对比分析 | 同集群多节点指标差异 | node-03 KV Cache > node-01 × 2.3 |
三、排障 Agent 的工程实现
3.1 告警触发与任务拆解
# diagnosis_agent.py —— 排障 Agent 核心
import asyncio
import json
import logging
from datetime import datetime, timedelta
from typing import List, Dict, Optional, Any
from dataclasses import dataclass
from openai import AsyncOpenAI
logger = logging.getLogger(__name__)
@dataclass
class DiagnosisStep:
"""排查步骤"""
step_id: int
action: str # 工具调用名称
args: Dict[str, Any] # 工具参数
reasoning: str # LLM 给出的执行理由
status: str # "pending" | "running" | "done" | "failed"
@dataclass
class DiagnosisReport:
"""诊断报告"""
alert_name: str
alert_time: datetime
affected_node: str
root_cause: str # 根因摘要
confidence: float # 置信度 0~1
evidence: List[str] # 支撑证据
recommendation: str # 修复建议
steps_executed: List[DiagnosisStep]
total_time_seconds: float
class DiagnosisAgent:
"""
排障 Agent
收到告警后,通过 LLM 规划排查步骤,
依次调用工具执行,最后生成诊断报告。
"""
# 可用工具定义(供 LLM 选择)
TOOLS = [
{
"name": "query_prometheus",
"description": "查询 Prometheus 指标(PromQL)",
"parameters": {
"query": "PromQL 查询语句",
"range_minutes": "查询时间范围(分钟)",
},
},
{
"name": "query_loki_logs",
"description": "查询 Loki 日志",
"parameters": {
"query": "LogQL 查询语句",
"start_time": "起始时间(ISO 8601)",
"end_time": "结束时间(ISO 8601)",
"limit": "返回条数上限",
},
},
{
"name": "compare_nodes",
"description": "对比集群中不同节点的指标差异",
"parameters": {
"metric": "指标名(如 gpu_utilization)",
"target_node": "目标节点名",
"time_range_minutes": "对比时间范围",
},
},
{
"name": "check_recent_deployments",
"description": "检查最近的代码/配置变更",
"parameters": {
"service": "服务名",
"hours_back": "回溯时间(小时)",
},
},
{
"name": "exec_kubectl",
"description": "在指定 Pod 内执行命令",
"parameters": {
"pod_name": "Pod 名称",
"command": "要执行的命令",
},
},
]
def __init__(self, openai_client: AsyncOpenAI):
self.client = openai_client
self.tool_handlers = {
"query_prometheus": self._handle_prometheus,
"query_loki_logs": self._handle_loki,
"compare_nodes": self._handle_compare_nodes,
"check_recent_deployments": self._handle_deployments,
"exec_kubectl": self._handle_kubectl,
}
async def diagnose(
self, alert: Dict
) -> DiagnosisReport:
"""
对一条告警执行自动化诊断
Args:
alert: {
"name": "VLLMHighTTFT",
"labels": {"instance": "gpu-node-03:8000",
"model": "qwen2-72b"},
"annotations": {"summary": "...",
"description": "..."},
"startsAt": "2024-07-15T03:00:00Z",
}
Returns:
DiagnosisReport 诊断报告
"""
start_time = datetime.utcnow()
steps = []
# =============================================
# 阶段 1: 任务规划
# LLM 将告警转化为具体的排查步骤序列
# =============================================
plan = await self._plan_steps(alert)
logger.info(f"排障计划: {len(plan)} 步")
for s in plan:
logger.info(f" Step {s.step_id}: {s.action} - {s.reasoning}")
# =============================================
# 阶段 2: 逐步执行
# =============================================
findings = [] # 收集每步的发现
for step in plan:
step.status = "running"
try:
handler = self.tool_handlers.get(step.action)
if handler is None:
step.status = "failed"
findings.append(
f"Step {step.step_id}: 未知工具 {step.action}")
continue
result = await handler(step.args)
step.status = "done"
findings.append(
f"Step {step.step_id} ({step.action}): {result}")
except Exception as e:
step.status = "failed"
findings.append(
f"Step {step.step_id} 失败: {str(e)}")
steps.append(step)
# =============================================
# 阶段 3: 根因推断
# LLM 综合分析所有发现,推断根因
# =============================================
root_cause, confidence, recommendation = \
await self._infer_root_cause(
alert, findings)
elapsed = (datetime.utcnow() - start_time).total_seconds()
return DiagnosisReport(
alert_name=alert["name"],
alert_time=datetime.fromisoformat(
alert["startsAt"].replace("Z", "+00:00")),
affected_node=alert["labels"]["instance"],
root_cause=root_cause,
confidence=confidence,
evidence=findings,
recommendation=recommendation,
steps_executed=steps,
total_time_seconds=elapsed,
)
async def _plan_steps(
self, alert: Dict
) -> List[DiagnosisStep]:
"""
让 LLM 根据告警信息规划排查步骤
输入:告警名称、描述、影响范围、时间
输出:5~8 个具体的排查步骤
"""
prompt = f"""你是一位 SRE 排障专家。收到以下告警:
告警名称: {alert['name']}
告警节点: {alert['labels']['instance']}
告警描述: {alert['annotations']['description']}
告警时间: {alert['startsAt']}
请规划排查步骤,使用以下工具:
{json.dumps(self.TOOLS, indent=2, ensure_ascii=False)}
规则:
1. 优先查询目标节点的关键指标(GPU 利用率、显存、QPS)
2. 对比同集群其他节点,判断是否单点问题
3. 检索相关时间段的错误日志
4. 检查最近的部署/配置变更
5. 每步需要清晰的执行理由
返回 JSON 格式:
[{{
"step_id": 1,
"action": "query_prometheus",
"args": {{"query": "PROMQL", "range_minutes": 30}},
"reasoning": "检查 GPU 利用率和显存使用趋势"
}}]"""
response = await self.client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"},
temperature=0.1, # 低温度,确保规划稳定
)
plan_data = json.loads(
response.choices[0].message.content)
return [
DiagnosisStep(
step_id=item["step_id"],
action=item["action"],
args=item["args"],
reasoning=item["reasoning"],
status="pending",
)
for item in plan_data.get("steps", [])
]
async def _infer_root_cause(
self,
alert: Dict,
findings: List[str],
) -> tuple:
"""综合分析,推断根因"""
prompt = f"""根据以下告警信息和排查发现,推断根因:
告警: {alert['name']} on {alert['labels']['instance']}
排查发现:
{chr(10).join(f"- {f}" for f in findings)}
请返回 JSON:
{{
"root_cause": "根因描述(一句话)",
"confidence": 0.85,
"recommendation": "修复建议"
}}
规则:
- root_cause 必须具体,不能是"可能是 XX"(给出最可能的原因)
- confidence 根据证据的充分程度给出(0~1)
- 如果证据不足以确定根因,标明还需要哪些信息"""
response = await self.client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"},
temperature=0.1,
)
result = json.loads(
response.choices[0].message.content)
return (
result.get("root_cause", "无法确定"),
result.get("confidence", 0.0),
result.get("recommendation", "请人工介入"),
)
# ==============================================
# 工具处理器(简化实现)
# ==============================================
async def _handle_prometheus(self, args):
"""查询 Prometheus"""
# 实际调用 Prometheus HTTP API
return f"PromQL: {args['query']} → 结果: [模拟数据]"
async def _handle_loki(self, args):
"""查询 Loki 日志"""
return f"LogQL: {args['query']} → 找到 15 条日志"
async def _handle_compare_nodes(self, args):
"""对比节点指标"""
return f"node-03 {args['metric']} 比其他节点高 2.3 倍"
async def _handle_deployments(self, args):
"""检查部署变更"""
return "最近 24h 无部署变更"
async def _handle_kubectl(self, args):
"""在 Pod 中执行命令"""
return f"kubectl exec {args['pod_name']} -- {args['command']} → OK"
3.2 告警严重度分级与自动修复边界
排障 Agent 生成诊断报告后,根据严重度和置信度决定后续动作:
# auto_remediation.py —— 自动修复决策
class RemediationPolicy:
"""
根据诊断结果决定是自动修复还是人工介入
规则:
- P0 告警 + 高置信度(>0.9) → 自动执行修复 + 通知
- P0 告警 + 低置信度 → 通知 oncall(附带诊断报告)
- P1 告警 + 高置信度 → 提出修复建议,等待确认
- P1 告警 + 低置信度 → 仅通知
- P2 告警 → 记录不通知
"""
@staticmethod
def decide(
severity: str, # P0/P1/P2
confidence: float,
) -> str:
if severity == "P0" and confidence > 0.9:
return "auto_fix_and_notify"
elif severity == "P0":
return "notify_oncall"
elif severity == "P1" and confidence > 0.9:
return "suggest_fix"
else:
return "log_only"
四、排障 Agent 的边界与风险
4.1 幻觉风险——LLM 自信地给出错误结论
排障 Agent 最大的风险是 LLM 的"幻觉"——在证据不足时编造根因。例如看到"GPU 显存用量 95%"就断言"显存泄漏",但实际是正常的 KV Cache 使用。
缓解方式:
- 置信度低于 0.7 的结论自动升级为人工介入。
- 所有诊断报告的结论必须附带"支撑证据"列表,方便人工复核。
- Agent 不直接执行危险操作(如重启 Pod、释放显存),必须人工确认。
4.2 Token 成本
每次排障的 LLM 调用(规划 + 推断)约消耗 3000~5000 tokens。按 GPT-4 的 API 价格,每次诊断约 0.05~0.10 美元。按每天 20 次 P1/P2 告警计算,月成本约 60 美元——远低于一名 SRE 工程师的时薪。
4.3 工具集的可扩展性
排障 Agent 的能力上限取决于提供的工具数量和质量。如果某个排查需要的工具不在 TOOLS 列表中,Agent 就无法完成诊断——LLM 的规划能力再强也受限于工具边界。定期 Review 未解决的告警,补充新的诊断工具。
五、总结
排障 Agent 的目标不是替代 SRE,而是将机械性的"查指标 → 查日志 → 对比分析"自动化:
- 任务规划 LLM:将告警转化为 5~8 个具体的排查步骤,每步选择最合适的工具。
- 工具执行链:依次调用 Prometheus、Loki、kubectl 等工具接口,串行收集证据。
- 根因推断 LLM:综合分析所有排查发现,给出具体根因和修复建议,附带置信度。
- 分级的自动修复:高置信度 + 低风险操作(如扩容)可自动执行;其他情况保持人工介入。
更多推荐



所有评论(0)