从自然语言SOP到自动化故障处置:LLM Agent如何将纸面化Runbook转化为可执行操作序列的全链路拆解
从自然语言SOP到自动化故障处置LLM Agent如何将纸面化Runbook转化为可执行操作序列的全链路拆解一、凌晨3:17的故障处置场景为什么Runbook经常是有等于无凌晨 3:17支付网关的 P99 延迟飙升至 12 秒告警已触发。值班运维从床上爬起来打开企业 Wiki找到《支付网关高延迟故障处置 Runbook v3.2》。文档写了满满 5 页包含步骤一检查数据库连接池状态、步骤二确认消息队列积压情况等 12 个操作步骤。但问题在于——这只是自然语言描述的 SOP而非可执行的操作序列。运维人员需要将每个自然语言步骤翻译成具体的命令、API 调用和参数组合。这意味着在凌晨 3 点的高压状态下需要手动完成以下转换将检查数据库连接池状态翻译为kubectl exec -it mysql-0 -- mysql -e SHOW PROCESSLIST解析输出判断连接数是否接近max_connections。将确认消息队列积压情况翻译为kafka-consumer-groups --bootstrap-server kafka:9092 --group payment-processor --describe提取LAG值判断积压是否超过阈值。将如果连接池正常且无消息积压检查上游服务翻译为链路追踪查询和依赖服务的健康检查。一个有经验的运维可能需要 10-15 分钟完成全套排查。但关键问题不是时间而是信息到动作的转换链路太长——每一步都是一个潜在的出错点。凌晨 3 点键入kubectl delete pod的手比白天更容易点错目标。这就是 LLM Agent Runbook 的切入点让 LLM 理解自然语言 SOP 的语义自动将操作步骤映射到工具调用序列并在执行前进行安全验证。将运维的知识资产Runbook从参考文档升级为可执行的自动化脚本。二、从自然语言到工具调用的完整转换流水线这个流水线的核心是用 LLM 的语义理解能力来桥接自然语言 SOP和具体工具调用之间的鸿沟。具体的工程实现分为四个层级第一层SOP 步骤分解。Runbook 文档通常包含大小标题、编号列表、条件分支。首先需要将文档解析为结构化的步骤树每个叶节点对应一个可独立执行的检查操作。这一步可以用 LLM 做也可以用传统的 Markdown 解析器——推荐后者以节约 Token。第二层意图分类与工具路由。每个步骤被分类为预定义的意图类型数据库检查、消息队列检查、链路检查等然后路由到对应的工具集。这一步的工程挑战在于边界情况当 LLM 遇到检查支付网关的 Redis 缓存命中率这种跨领域的步骤时需要将其拆解为连接 Redis → 执行 INFO stats → 解析输出 → 计算命中率的多个原子工具调用。第三层安全执行与权限隔离。所有工具调用必须在沙箱环境中执行操作被分为三级只读操作自动执行、修改操作需人工确认、危险操作拒绝自动执行仅输出建议。这一层不是 LLM 的强项需要硬编码的安全策略规则。第四层结果聚合与根因推断。所有检查步骤的输出被汇总为结构化的诊断报告然后调用 LLM 进行根因推断。与第一版 RAG 方案的关键区别是此时 LLM 已经不是凭空猜测而是基于实际采集的实时数据进行分析。三、代码实现一个最小化的 Runbook Agent 引擎 Runbook Agent 引擎将自然语言 SOP 转换为可执行的工具调用序列 核心设计原则 1. 只读操作为主修改操作需要人工确认 2. 每个工具的输入输出都有严格的 Schema 校验 3. 执行失败时自动进入人工介入通道而非盲目重试 import json import re from typing import List, Dict, Optional, Callable, Any, Tuple from dataclasses import dataclass, field from enum import Enum from abc import ABC, abstractmethod class ActionLevel(Enum): READONLY readonly # 只读操作自动执行 MUTATING mutating # 修改操作需人工确认 DANGEROUS dangerous # 危险操作只输出建议不执行 class StepResult(Enum): SUCCESS success FAILED failed NEEDS_CONFIRMATION needs_confirmation DENIED denied dataclass class SOPStep: Runbook 中的单一步骤 step_id: str # 步骤编号如 2.1 description: str # 自然语言描述 intent: str # 意图类型 tools: List[str] # 需要的工具列表 expected_output: str # 预期输出模式 action_level: ActionLevel ActionLevel.READONLY on_failure: str escalate # 失败处理策略 dataclass class ToolResult: 工具调用的返回结果 tool_name: str success: bool output: Any error: Optional[str] None duration_ms: float 0.0 dataclass class DiagnosisReport: 自动化诊断的完整报告 alert_context: Dict[str, Any] steps_executed: List[Dict[str, Any]] field(default_factorylist) root_cause_hypothesis: Optional[str] None confidence: float 0.0 recommendation: str requires_human_review: bool True # 工具注册与安全策略 # 工具安全等级映射表 # 危险操作硬编码拒绝不依赖 LLM 判断 TOOL_SAFETY_POLICY: Dict[str, ActionLevel] { kubectl_exec_mysql_query: ActionLevel.READONLY, kubectl_get_pod_logs: ActionLevel.READONLY, kubectl_get_pod_status: ActionLevel.READONLY, kafka_consumer_group_describe: ActionLevel.READONLY, prometheus_instant_query: ActionLevel.READONLY, jaeger_trace_search: ActionLevel.READONLY, kubectl_scale_deployment: ActionLevel.MUTATING, kubectl_rollout_restart: ActionLevel.MUTATING, kubectl_delete_pod: ActionLevel.DANGEROUS, kubectl_exec_rm: ActionLevel.DANGEROUS, } class ToolRegistry: 工具注册中心管理所有可用工具及其安全等级 def __init__(self): self._tools: Dict[str, Callable] {} def register(self, name: str, func: Callable) - None: 注册一个工具函数 self._tools[name] func def get_action_level(self, tool_name: str) - ActionLevel: 查询工具的安全等级未注册的工具默认为 DANGEROUS return TOOL_SAFETY_POLICY.get(tool_name, ActionLevel.DANGEROUS) def execute(self, tool_name: str, **kwargs) - ToolResult: 执行工具调用带安全检查 Args: tool_name: 工具名称 **kwargs: 工具参数 Returns: 工具执行结果 import time start time.time() if tool_name not in self._tools: return ToolResult( tool_nametool_name, successFalse, errorf工具 {tool_name} 未注册 ) # 安全等级检查在调用方Agent进行 # 此处仅做执行和异常捕获 try: output self._tools[tool_name](**kwargs) elapsed (time.time() - start) * 1000 return ToolResult( tool_nametool_name, successTrue, outputoutput, duration_mselapsed, ) except Exception as e: elapsed (time.time() - start) * 1000 return ToolResult( tool_nametool_name, successFalse, errorstr(e), duration_mselapsed, ) # 模拟工具实现 def tool_kubectl_get_pod_status(namespace: str, label_selector: str) - Dict: 查询指定命名空间中 Pod 的状态 返回 Pod 列表及其状态Running/Pending/CrashLoopBackOff 等 # 生产环境中会调用 kubectl Python client 或直接调用 API Server return { namespace: namespace, selector: label_selector, pods: [ {name: payment-gateway-7d5f8b9c-abc12, status: Running, restarts: 2, age: 3d}, {name: payment-gateway-7d5f8b9c-def34, status: Running, restarts: 0, age: 1h}, ], total: 2, ready: 2, } def tool_kafka_consumer_lag(bootstrap: str, group: str) - Dict: 查询 Kafka 消费者组的 Lag 情况 如果 Lag 持续增长说明消费者处理速度跟不上生产速度 return { group: group, topic: payment-events, partitions: [ {partition: 0, current_offset: 1456789, log_end_offset: 1456790, lag: 1}, {partition: 1, current_offset: 983421, log_end_offset: 984200, lag: 779}, ], total_lag: 780, status: WARNING, # partition 1 存在积压 } def tool_prometheus_query(query: str) - Dict: 执行 PromQL 即时查询 返回查询结果和元数据 return { query: query, result_type: vector, results: [ {metric: {pod: payment-gateway-7d5f8b9c-abc12}, value: [1715800000, 320]}, ], summary: 当前活跃连接数: 320 / 最大允许: 500, } # Runbook Agent 核心引擎 class RunbookAgent: Runbook 自动化执行引擎 核心流程 1. 解析 Runbook 步骤 2. 对每个步骤执行工具调用 3. 汇总结果并进行根因推断 4. 输出诊断报告 def __init__(self, tool_registry: ToolRegistry): self.tools tool_registry self.confirmation_callback: Optional[Callable] None def parse_runbook(self, runbook_text: str) - List[SOPStep]: 将自然语言 Runbook 解析为结构化步骤列表 使用正则匹配编号步骤提取意图和工具映射 steps [] # 匹配形如 步骤一、2.、2.1 的步骤标题 pattern r(?:步骤\s*([一二三四五六七八九十\d])|(?:^|\n)\s*(\d(?:\.\d)?)\s*[.、)]) # 简化按段落分割每个段落的标题作为 step_id return self._llm_parse_runbook(runbook_text) def _llm_parse_runbook(self, runbook_text: str) - List[SOPStep]: 使用 LLM 将 Runbook 文本解析为结构化的 SOPStep 列表 这里使用预定义的步骤模板简化实现 生产环境需要调用实际的 LLM API 进行解析 # 模拟 LLM 解析结果 steps [ SOPStep( step_id1, description检查 payment-gateway Pod 运行状态, intentpod_health_check, tools[kubectl_get_pod_status], expected_output所有 Pod 状态为 Running, ), SOPStep( step_id2, description确认 Kafka 消费者组积压情况, intentkafka_lag_check, tools[kafka_consumer_group_describe], expected_output所有分区 Lag 1000, ), SOPStep( step_id3, description查询数据库连接池当前活跃连接数, intentdb_pool_check, tools[prometheus_instant_query], expected_output活跃连接数 max_connections 的 80%, ), SOPStep( step_id4, description若以上均正常尝试重启 payment-gateway 以释放可能的资源泄漏, intentremediation, tools[kubectl_rollout_restart], expected_output重启成功, action_levelActionLevel.MUTATING, on_failureescalate_to_human, ), ] return steps def execute_step(self, step: SOPStep, context: Dict) - Dict: 执行 Runbook 中的单个步骤 根据安全等级决定执行策略 - READONLY: 自动执行 - MUTATING: 检查是否需要人工确认 - DANGEROUS: 拒绝执行仅输出建议 Args: step: SOP 步骤对象 context: 诊断上下文告警信息、已执行步骤的结果等 Returns: 步骤执行结果 # 安全等级前置检查 level ActionLevel.READONLY for tool_name in step.tools: tool_level self.tools.get_action_level(tool_name) # 取步骤中所有工具的最高安全等级 if tool_level.value level.value: level tool_level results [] for tool_name in step.tools: if level ActionLevel.DANGEROUS: results.append({ tool: tool_name, result: denied, message: f工具 {tool_name} 被安全策略拒绝自动执行 f已转为人工操作建议, }) continue if level ActionLevel.MUTATING: # 需要人工确认的操作 if self.confirmation_callback: approved self.confirmation_callback(step, context) if not approved: results.append({ tool: tool_name, result: needs_confirmation, message: 等待人工确认中, }) continue # 构建工具调用参数生产环境中由 LLM 生成 params self._build_tool_params(tool_name, context) result self.tools.execute(tool_name, **params) results.append({ tool: tool_name, success: result.success, output: result.output, error: result.error, duration_ms: result.duration_ms, }) all_success all(r.get(success, False) for r in results) return { step_id: step.step_id, description: step.description, action_level: level.value, results: results, overall: success if all_success else failed, } def _build_tool_params(self, tool_name: str, context: Dict) - Dict: 根据工具名称和上下文构建调用参数 生产环境中此处应调用 LLM 根据 Context 和 Step 描述生成参数 params_map { kubectl_get_pod_status: { namespace: context.get(namespace, default), label_selector: apppayment-gateway, }, kafka_consumer_group_describe: { bootstrap: kafka-broker:9092, group: payment-processor, }, prometheus_instant_query: { query: mysql_global_status_threads_connected, }, } return params_map.get(tool_name, {}) def diagnose(self, alert: Dict, runbook_text: str) - DiagnosisReport: 完整的自动化诊断流程 Args: alert: 告警信息服务名、指标、当前值等 runbook_text: 对应的 Runbook 文本 Returns: 完整的诊断报告 report DiagnosisReport(alert_contextalert) # 1. 解析 Runbook steps self.parse_runbook(runbook_text) # 2. 逐步执行 context alert.copy() for step in steps: step_result self.execute_step(step, context) report.steps_executed.append(step_result) # 将当前步骤的结果合并到上下文中供后续步骤参考 context[fstep_{step.step_id}] step_result # 如果某步骤失败且策略要求升级停止自动诊断 if step_result[overall] failed and step.on_failure escalate_to_human: report.recommendation ( f步骤 {step.step_id} 执行失败诊断已自动升级。 f请人工检查以下输出\n{json.dumps(step_result, indent2, ensure_asciiFalse)} ) report.requires_human_review True return report # 3. 汇总所有结果调用 LLM 进行根因推断 summary self._summarize_results(report.steps_executed) report.root_cause_hypothesis summary[hypothesis] report.confidence summary[confidence] report.recommendation summary[recommendation] # 4. 置信度低于阈值时标记需要人工审核 report.requires_human_review report.confidence 0.75 return report def _summarize_results(self, step_results: List[Dict]) - Dict: 汇总各步骤的诊断结果进行根因推断 生产环境需调用 LLM API 完成模拟推理 # 模拟分析逻辑 has_kafka_lag any( lag in str(r) for step in step_results for r in step.get(results, []) ) if has_kafka_lag: return { hypothesis: Kafka partition 1 存在消息积压Lag779 推测为消费者处理变慢导致支付延迟, confidence: 0.82, recommendation: 建议扩容 payment-processor 消费者实例数 或检查消费者 GC 日志确认是否存在 Full GC 停顿, } else: return { hypothesis: 所有检查项均无异常问题可能在上游依赖或网络层, confidence: 0.45, recommendation: 建议手动检查上游 API Gateway 和网络链路状态, } # 使用示例 if __name__ __main__: # 初始化工具注册中心 registry ToolRegistry() registry.register(kubectl_get_pod_status, tool_kubectl_get_pod_status) registry.register(kafka_consumer_group_describe, tool_kafka_consumer_lag) registry.register(prometheus_instant_query, tool_prometheus_query) # 初始化 Agent agent RunbookAgent(registry) # 模拟告警 alert { service: payment-gateway, metric: p99_latency, value: 12.0, unit: seconds, threshold: 5.0, namespace: production, timestamp: 2026-07-16T03:17:00Z, } # 对应的 Runbook 文本简化 runbook # 支付网关高延迟故障处置 SOP ## 步骤一检查 Pod 状态 检查 payment-gateway 所有 Pod 的运行状态确认无 CrashLoopBackOff 或 Pending。 ## 步骤二确认消息队列积压 使用 kafka-consumer-groups 命令检查 payment-processor 消费者组的 Lag 值。 ## 步骤三数据库连接池检查 查询 MySQL 当前活跃连接数确认未超过 max_connections 的 80%。 ## 步骤四重启服务 如果以上检查均正常尝试滚动重启 payment-gateway 释放可能的连接泄漏。 # 执行诊断 report agent.diagnose(alert, runbook) print(f根因推断: {report.root_cause_hypothesis}) print(f置信度: {report.confidence}) print(f建议: {report.recommendation}) print(f需要人工审核: {report.requires_human_review})四、Agent 自动化处置的不可逾越红线Runbook Agent 的能力边界必须被严格限定。以下四条是建议的硬约束禁止直接操作生产数据库的写操作。Agent 可以执行SHOW PROCESSLIST和EXPLAIN但绝对不能执行KILL CONNECTION或任何 DDL/DML 语句。数据库的连接管理需要理解业务上下文的运维人员来做决策。禁止修改集群级别的安全配置。kubectl edit networkpolicy、修改 RBAC 规则、变更 Pod Security Policy——这些操作涉及安全边界Agent 的最高操作权限应限定在提供建议的命令层级。操作必须具有可逆性。滚动重启是安全的自动创建新 Pod旧 Pod 优雅终止但kubectl delete pvc删除持久卷声明数据不可恢复必须被拦截。Agent 的任何修改操作都必须具备自动或手动的回滚路径。故障处置后的验证闭环。Agent 执行操作后必须自动触发一轮验证检查——确认受影响服务的指标是否恢复正常、是否有新的异常告警产生。如果验证失败Agent 应自动回滚操作并升级给人工处理。五、总结LLM Agent Runbook 的自动化故障处置方案本质上是将运维团队积累的隐性知识Runbook 文档中的操作经验转化为可执行的显性自动化。它不能替代资深运维的判断力但可以在凌晨 3 点的高压时刻将需要查什么、怎么查、顺序是什么这三个最常见的决策负担自动化掉。落地建议分两个阶段推进第一阶段对标并行Agent 在旁路模式运行——它读取告警和 Runbook执行诊断输出诊断报告但所有操作建议仅推送给人工不真正执行。通过对比 Agent 诊断结论与人工排查结论校准 Agent 的准确率。至少要积累 50 次以上的Agent vs 人工对比数据后才进入下一阶段。第二阶段有限自动执行只读诊断步骤全自动执行修改操作需人工确认。同时建立操作审计日志记录 Agent 的每一次工具调用和决策链路确保故障复盘时可以回溯完整的诊断过程。最终的目标是让 Runbook Agent 成为运维团队的外挂第二大脑在不增加人员负担的前提下将故障发现到首次诊断的时间从分钟级缩短到秒级。