AI 自动化运维平台架构:从 LLM Agent 到自愈闭环的工程化落地

cover

一、运维自动化的瓶颈:脚本编排的灵活性与 L1/L2 工单的响应延迟

传统运维自动化的典型形态是"脚本 + Runbook":运维工程师编写 Shell/Python 脚本处理常见故障,通过 Runbook 文档定义操作步骤。这种模式在处理已知、重复性问题时效率很高,但面对未知故障或跨系统关联问题时,脚本编排的灵活性严重不足。

一个真实的场景:凌晨两点,值班工程师收到告警——订单服务的 P99 延迟从 200ms 飙升到 5s。他按照 Runbook 依次排查:检查 Pod 状态(正常)、检查 CPU/内存(正常)、检查数据库连接池(正常)、检查 Redis 延迟(发现异常)、检查 Redis 所在 Node 的磁盘 I/O(发现 I/O 等待高)、检查 Node 上是否有其他高 I/O 进程(发现一个日志采集 Agent 在做全量扫描)——整个排查过程耗时 40 分钟,最终根因是一个无关进程的磁盘 I/O 竞争。

这个案例暴露了传统运维自动化的核心瓶颈:每一步排查都需要人工决策"下一步查什么",而 L1/L2 工单的流转进一步增加了响应延迟。AI 自动化运维平台的目标,是将这种"人工决策 + 手动执行"的模式,转变为"AI 决策 + 自动执行"的闭环。

二、LLM Agent 与知识图谱:AI 运维平台的控制平面架构

AI 自动化运维平台的核心是一个基于 LLM(大语言模型)的 Agent 系统。LLM 充当"决策大脑",根据故障上下文和知识库中的历史经验,动态生成排查步骤和修复方案;Agent 框架负责编排工具调用、管理对话上下文、执行安全校验。

flowchart TB
    subgraph "告警接入层"
        A1[Prometheus Alertmanager] --> B[告警标准化与富化]
        A2[自定义 Webhook] --> B
        A3[日志异常检测] --> B
    end

    subgraph "AI 决策引擎"
        B --> C[故障上下文构建<br/>告警详情 + 拓扑关系 + 近期变更]
        C --> D[LLM Agent<br/>故障诊断与方案生成]
        D --> E[知识图谱检索<br/>历史相似故障 + 修复记录]
        E --> D
        D --> F[方案安全校验<br/>风险评估 + 人工审批]
    end

    subgraph "工具执行层"
        F --> G1[K8s 诊断工具<br/>kubectl/debug/describe]
        F --> G2[数据库操作<br/>慢查询分析/连接池检查]
        F --> G3[基础设施操作<br/>扩容/重启/流量切换]
        F --> G4[通知与协作<br/>工单创建/消息推送]
    end

    subgraph "自愈闭环"
        G1 --> H[执行结果反馈]
        G2 --> H
        G3 --> H
        H --> I{故障是否恢复?}
        I -->|否| C
        I -->|是| J[故障复盘与知识沉淀]
        J --> K[知识图谱更新]
        K --> E
    end

    subgraph "安全护栏"
        L[操作白名单] --> F
        M[人工审批网关<br/>高风险操作需确认] --> F
        N[操作回滚机制] --> G3
    end

LLM Agent 的工作机制。当告警触发后,Agent 首先构建故障上下文:包括告警详情、服务拓扑关系、近期变更记录、历史相似故障。然后将上下文组装为 Prompt,调用 LLM 生成诊断步骤。LLM 的输出不是自由文本,而是结构化的工具调用指令(如 kubectl describe podmysql slow_query),Agent 框架负责执行这些指令并将结果反馈给 LLM,形成多轮对话式的诊断闭环。

知识图谱的作用。LLM 的推理能力受限于训练数据的时效性和领域覆盖度。知识图谱存储了企业内部的历史故障记录、修复方案、系统架构拓扑等私有知识,通过 RAG(Retrieval-Augmented Generation)机制在 LLM 推理时提供上下文增强。当 LLM 遇到不熟悉的故障模式时,可以从知识图谱中检索相似案例,避免从零开始推理。

安全护栏。AI 运维平台的核心风险是"误操作"。LLM 可能生成错误的修复方案(如误删数据库、错误扩容),导致比原始故障更严重的后果。安全护栏包括三个层次:操作白名单(仅允许预定义的安全操作)、人工审批网关(高风险操作需人工确认)、操作回滚机制(执行前自动创建回滚点)。

三、AI 运维平台核心模块的工程实现

3.1 LLM Agent 框架

"""
AI 运维平台 LLM Agent 框架
基于 ReAct(Reasoning + Acting)模式的故障诊断 Agent
"""

import json
import re
import time
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Callable, Any
from enum import Enum
import logging

logger = logging.getLogger("aiops_agent")
logger.setLevel(logging.INFO)


class ToolRiskLevel(Enum):
    """工具风险等级"""
    SAFE = "safe"           # 只读操作,无需审批
    MODERATE = "moderate"   # 低影响写操作,需记录
    DANGEROUS = "dangerous" # 高影响写操作,需人工审批


@dataclass
class Tool:
    """工具定义"""
    name: str
    description: str
    risk_level: ToolRiskLevel
    execute_fn: Callable[[Dict], str]
    parameters: Dict = field(default_factory=dict)


@dataclass
class DiagnosticStep:
    """诊断步骤"""
    thought: str        # Agent 的推理过程
    tool_name: str      # 要调用的工具
    tool_input: Dict    # 工具输入参数
    observation: str = ""  # 工具执行结果


@dataclass
class FaultContext:
    """故障上下文"""
    alert_name: str
    alert_details: str
    affected_service: str
    namespace: str
    severity: str
    topology_info: str = ""
    recent_changes: str = ""
    similar_incidents: str = ""


class AIOpsAgent:
    """
    AI 运维 Agent
    基于 ReAct 模式:推理 -> 行动 -> 观察 -> 再推理
    """

    def __init__(self, llm_client, max_iterations: int = 10):
        self.llm_client = llm_client
        self.max_iterations = max_iterations
        self.tools: Dict[str, Tool] = {}
        self.history: List[DiagnosticStep] = []
        self.approval_callback: Optional[Callable] = None

    def register_tool(self, tool: Tool):
        """注册可用工具"""
        self.tools[tool.name] = tool
        logger.info(f"工具已注册: {tool.name} (风险: {tool.risk_level.value})")

    def set_approval_callback(self, callback: Callable):
        """设置人工审批回调函数"""
        self.approval_callback = callback

    def _build_system_prompt(self, context: FaultContext) -> str:
        """构建系统 Prompt"""
        tool_descriptions = "\n".join([
            f"- {name}: {tool.description} "
            f"[风险等级: {tool.risk_level.value}]"
            for name, tool in self.tools.items()
        ])

        return f"""你是一个专业的运维诊断 Agent。根据故障上下文,逐步分析问题根因并生成修复方案。

可用工具:
{tool_descriptions}

故障上下文:
- 告警名称: {context.alert_name}
- 告警详情: {context.alert_details}
- 受影响服务: {context.affected_service}
- 命名空间: {context.namespace}
- 严重级别: {context.severity}
- 拓扑信息: {context.topology_info}
- 近期变更: {context.recent_changes}
- 相似历史故障: {context.similar_incidents}

请按照以下格式输出每一步:
Thought: [你的推理过程]
Action: [工具名称]
Action Input: [JSON 格式的工具参数]

当你确定根因后,输出:
Thought: [最终推理结论]
Final Answer: [根因分析和修复方案]
"""

    def _parse_llm_response(self, response: str) -> DiagnosticStep:
        """解析 LLM 的响应,提取推理、工具和参数"""
        thought = ""
        action = ""
        action_input = {}

        # 提取 Thought
        thought_match = re.search(r"Thought:\s*(.+?)(?:\n|$)", response)
        if thought_match:
            thought = thought_match.group(1).strip()

        # 提取 Action
        action_match = re.search(r"Action:\s*(.+?)(?:\n|$)", response)
        if action_match:
            action = action_match.group(1).strip()

        # 提取 Action Input
        input_match = re.search(r"Action Input:\s*(.+?)(?:\n|$)", response, re.DOTALL)
        if input_match:
            try:
                action_input = json.loads(input_match.group(1).strip())
            except json.JSONDecodeError:
                action_input = {"raw_input": input_match.group(1).strip()}

        return DiagnosticStep(
            thought=thought,
            tool_name=action,
            tool_input=action_input
        )

    def _execute_tool(self, step: DiagnosticStep) -> str:
        """执行工具调用,包含安全校验"""
        tool = self.tools.get(step.tool_name)
        if not tool:
            return f"错误: 工具 '{step.tool_name}' 不存在"

        # 安全校验:高风险操作需人工审批
        if tool.risk_level == ToolRiskLevel.DANGEROUS:
            if self.approval_callback:
                approved = self.approval_callback(
                    f"高风险操作请求:\n"
                    f"工具: {step.tool_name}\n"
                    f"参数: {json.dumps(step.tool_input, ensure_ascii=False)}\n"
                    f"推理: {step.thought}\n"
                    f"是否批准执行?"
                )
                if not approved:
                    return "操作已被人工审批拒绝"
            else:
                return "错误: 高风险操作需要人工审批,但未配置审批回调"

        # 执行工具
        try:
            result = tool.execute_fn(step.tool_input)
            logger.info(f"工具执行成功: {step.tool_name}")
            return result
        except Exception as e:
            logger.error(f"工具执行失败: {step.tool_name} - {str(e)}")
            return f"工具执行异常: {str(e)}"

    def diagnose(self, context: FaultContext) -> Dict:
        """
        执行故障诊断
        返回诊断结果、执行步骤和修复方案
        """
        system_prompt = self._build_system_prompt(context)
        conversation = [{"role": "system", "content": system_prompt}]

        for iteration in range(self.max_iterations):
            logger.info(f"--- 诊断轮次 {iteration + 1}/{self.max_iterations} ---")

            # 调用 LLM
            response = self.llm_client.chat(conversation)
            step = self._parse_llm_response(response)

            # 检查是否已得出最终结论
            if "Final Answer:" in response:
                final_match = re.search(
                    r"Final Answer:\s*(.+)", response, re.DOTALL
                )
                final_answer = final_match.group(1).strip() if final_match else ""
                return {
                    "status": "completed",
                    "root_cause": step.thought,
                    "remediation": final_answer,
                    "steps": self.history,
                    "iterations": iteration + 1
                }

            # 执行工具
            observation = self._execute_tool(step)
            step.observation = observation
            self.history.append(step)

            # 将结果反馈给 LLM
            conversation.append({"role": "assistant", "content": response})
            conversation.append({
                "role": "user",
                "content": f"Observation: {observation}"
            })

        # 达到最大轮次仍未解决
        return {
            "status": "max_iterations_reached",
            "steps": self.history,
            "iterations": self.max_iterations
        }


# ===== 工具定义示例 =====
def kubectl_describe_pod(params: Dict) -> str:
    """模拟 kubectl describe pod 命令"""
    pod_name = params.get("pod_name", "")
    namespace = params.get("namespace", "default")
    # 实际实现中调用 subprocess 执行 kubectl 命令
    return (
        f"Pod: {pod_name} (Namespace: {namespace})\n"
        f"Status: Running\n"
        f"Restarts: 3\n"
        f"Last State: Terminated with OOMKilled (exit code 137)\n"
        f"Memory Limit: 512Mi, Memory Usage: 524Mi (exceeded)"
    )


def check_db_connections(params: Dict) -> str:
    """模拟数据库连接池检查"""
    service = params.get("service", "")
    return (
        f"数据库连接池状态 ({service}):\n"
        f"Active: 195/200 (97.5%)\n"
        f"Waiting: 45\n"
        f"Max Wait Time: 12.3s\n"
        f"警告: 连接池接近耗尽"
    )


def scale_deployment(params: Dict) -> str:
    """模拟 K8s Deployment 扩容"""
    deployment = params.get("deployment", "")
    replicas = params.get("replicas", 3)
    namespace = params.get("namespace", "default")
    return (
        f"Deployment {deployment}/{namespace} "
        f"已扩容至 {replicas} 副本"
    )


# ===== 使用示例 =====
if __name__ == "__main__":
    # 模拟 LLM 客户端(实际实现中对接 OpenAI/Claude API)
    class MockLLMClient:
        def chat(self, messages):
            # 模拟 LLM 的多轮推理过程
            last_msg = messages[-1]["content"] if messages else ""
            if "Observation" not in last_msg:
                return (
                    "Thought: 订单服务延迟飙升,先检查 Pod 状态\n"
                    "Action: kubectl_describe_pod\n"
                    'Action Input: {"pod_name": "order-service-7d8f9", '
                    '"namespace": "production"}'
                )
            elif "OOMKilled" in last_msg:
                return (
                    "Thought: Pod 因 OOM 被杀,检查数据库连接池状态\n"
                    "Action: check_db_connections\n"
                    'Action Input: {"service": "order-service"}'
                )
            elif "连接池接近耗尽" in last_msg:
                return (
                    "Thought: Pod OOM 且数据库连接池接近耗尽,"
                    "可能是内存泄漏导致连接未释放。"
                    "建议先扩容缓解压力,再排查内存泄漏\n"
                    "Final Answer: 根因: order-service 存在内存泄漏,"
                    "导致 Pod OOM 重启和数据库连接池耗尽。"
                    "修复方案: 1) 立即扩容至 5 副本缓解压力; "
                    "2) 排查近期代码变更中的连接泄漏问题; "
                    "3) 临时增大内存限制至 1Gi"
                )
            return "Thought: 需要更多信息\nAction: kubectl_describe_pod\nAction Input: {}"

    # 创建 Agent 并注册工具
    agent = AIOpsAgent(llm_client=MockLLMClient(), max_iterations=10)

    agent.register_tool(Tool(
        name="kubectl_describe_pod",
        description="查看 Pod 详细状态,包括事件、资源使用和重启原因",
        risk_level=ToolRiskLevel.SAFE,
        execute_fn=kubectl_describe_pod
    ))

    agent.register_tool(Tool(
        name="check_db_connections",
        description="检查数据库连接池状态,包括活跃连接数和等待数",
        risk_level=ToolRiskLevel.SAFE,
        execute_fn=check_db_connections
    ))

    agent.register_tool(Tool(
        name="scale_deployment",
        description="扩容 K8s Deployment 副本数",
        risk_level=ToolRiskLevel.DANGEROUS,
        execute_fn=scale_deployment
    ))

    # 设置人工审批回调
    agent.set_approval_callback(
        lambda msg: input(f"\n[审批] {msg}\n批准? (y/n): ").lower() == "y"
    )

    # 构建故障上下文
    context = FaultContext(
        alert_name="OrderServiceLatencyHigh",
        alert_details="P99 延迟从 200ms 飙升至 5000ms,持续 10 分钟",
        affected_service="order-service",
        namespace="production",
        severity="critical",
        topology_info="order-service -> db-primary, redis-cluster",
        recent_changes="2 小时前部署了 v2.3.1 版本",
        similar_incidents="3 个月前类似故障,根因为连接泄漏"
    )

    # 执行诊断
    result = agent.diagnose(context)
    print(f"\n诊断状态: {result['status']}")
    print(f"诊断轮次: {result['iterations']}")
    if result['status'] == 'completed':
        print(f"根因: {result['root_cause']}")
        print(f"修复方案: {result['remediation']}")

四、AI 运维的信任鸿沟:LLM 幻觉与自动化风险的工程边界

AI 自动化运维平台面临的最大挑战不是技术实现,而是运维团队对 AI 决策的信任问题。LLM 的幻觉(Hallucination)和推理错误可能导致错误的诊断结论和危险的修复操作。

LLM 幻觉的运维风险。LLM 可能"编造"不存在的故障原因,或推荐不适用于当前环境的修复方案。例如,LLM 可能建议"重启数据库主库"来缓解连接池压力,但忽略了主库重启会导致服务完全中断。缓解策略包括:将 LLM 的输出限制在结构化的工具调用格式内(而非自由文本),通过知识图谱提供准确的上下文信息,对 LLM 的输出进行后处理校验。

自动化执行的爆炸半径。AI 运维平台的自动化执行能力越强,误操作的爆炸半径越大。建议采用分级自动化策略:L0 级仅做诊断推荐,所有操作需人工执行;L1 级自动执行低风险操作(如 kubectl describe、日志查询),中高风险操作需人工审批;L2 级自动执行所有操作,但高风险操作有回滚保护。初期应从 L0 开始,逐步建立信任后升级到 L1。

知识图谱的维护成本。知识图谱的质量直接决定 RAG 增强的效果。如果知识图谱中存储的故障案例过时或不准确,LLM 可能基于错误的历史信息做出错误决策。建议将故障复盘流程与知识图谱更新联动,每次故障复盘后自动提取关键信息入库,并定期清理过时的知识条目。

LLM 推理延迟。当前主流 LLM 的推理延迟在 1-5 秒之间,多轮诊断可能需要 5-10 轮对话,总延迟在 10-50 秒。对于需要秒级响应的 P0 故障,这个延迟可能不可接受。建议将常见故障模式的诊断路径预编译为固定工作流,仅在遇到未知故障时才启动 LLM 推理。

成本控制。LLM API 的调用成本在多轮诊断场景下可能显著增加。一个复杂故障的诊断可能消耗 10-50K tokens,按 GPT-4 级别的定价约 $0.3-1.5/次。对于高频告警场景,建议使用轻量级模型(如 GPT-3.5)进行初步诊断,仅在需要深度推理时升级到高级模型。

五、总结

AI 自动化运维平台通过 LLM Agent + 知识图谱 + 安全护栏的架构,将故障诊断从"人工决策 + 手动执行"转变为"AI 推理 + 自动执行"的闭环。LLM 的推理能力使 Agent 能够处理未知故障场景,知识图谱提供企业私有知识的上下文增强,安全护栏确保自动化操作不会造成比原始故障更严重的后果。

落地路线:第一步,构建 L0 级别的诊断推荐系统,LLM 仅输出诊断步骤建议,所有操作由人工执行;第二步,注册安全的只读诊断工具(kubectl describe、日志查询、指标查询),实现 L1 级别的半自动化诊断;第三步,建立知识图谱,将历史故障复盘结果结构化入库,通过 RAG 增强 LLM 的诊断准确率;第四步,引入人工审批网关,逐步开放低风险写操作(如扩容、重启非核心服务);第五步,在信任度达标后,对高频故障模式启用 L2 级别的全自动修复,并配置回滚保护。

AI 运维不是替代运维工程师,而是将运维工程师从重复性诊断工作中解放出来,专注于架构优化和故障预防。在稳定性工程中,AI 运维平台的价值在于缩短故障响应时间、降低人为操作失误率、沉淀运维知识资产。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐