大模型 + 阿里云实例诊断: AI 赋能云运维的实践与心得

shabbywu大约 11 分钟llm

大模型 + 阿里云实例诊断:AI 赋能云运维的实践与心得

本文介绍我们如何利用大模型(LLM)结合 MCP Server 技术构建智能化的阿里云实例诊断系统。

文章重点分享了 MCP 工具设计的三个核心原则:使用 TOON 格式优化 Token 消耗、通过显式标注单位消除大模型幻觉、对时序监控数据进行预计算统计摘要。 同时介绍了 Prompt 工程实践,通过提供完整的输出格式模板来保证诊断报告的一致性。

一、背景与动机

传统云运维的痛点

在日常云资源运维中,运维工程师经常面临这些挑战:

  1. 信息分散:实例状态、监控数据、安全组规则、磁盘信息分布在不同的控制台页面
  2. 排查耗时:一个简单的"实例连不上"问题,可能需要检查十几个维度
  3. 经验依赖:新手难以快速定位问题,老手的经验难以沉淀和传承
  4. 重复劳动:相似问题反复排查,效率低下

我们的方案

我们希望打造一个极简的诊断入口:用户只需要提供一个实例 ID,系统就能自动完成全面诊断并输出专业报告。

输入:i-bp1xxxxxxxxxx(实例 ID)
输出:结构化的诊断报告(问题定位 + 解决建议)

在这个方案中,大模型承担两个核心职责

  1. 智能调度诊断流程

    • 大模型根据内置的诊断 Prompt 按需调用 MCP 工具
    • 自主决定调用顺序和调用哪些工具(不是固定流程)
    • 遇到异常数据时,能够自动深挖相关维度
  2. 输出用户友好的诊断报告

    • 将原始的 API 数据、监控指标转换为自然语言描述
    • 结构化呈现问题和建议,让非技术人员也能看懂
    • 提供可操作的解决方案,而不仅仅是数据罗列

这正是我们构建的——基于 Dify + MCP Server 的智能诊断系统。

二、技术架构

整体架构

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│                 │     │                 │     │                 │
│      Dify       │────▶│   MCP Server    │────▶│  GameCloud API  │
│   (模型编排)    │ MCP │    (提供工具)   │HTTP │   (云资源API)    │
│                 │◀────│                 │◀────│                 │
└─────────────────┘     └─────────────────┘     └─────────────────┘
        │                       │
        │                       │
        ▼                       ▼
   自然语言报告           ┌────────────────────────────┐
                        │   MCP Tools 工具集          │
                        ├────────────────────────────┤
                        │ • get_instance_detail      │
                        │ • get_instance_status      │
                        │ • get_monitor_data         │
                        │ • get_disks_info           │
                        │ • get_network_interfaces   │
                        │ • check_security_group     │
                        │ • get_console_log          │
                        │ • create_diagnostic_report │
                        └────────────────────────────┘

技术栈

层级技术选型说明
AI 编排层Dify低代码 AI 应用开发平台,支持 Agent 模式
协议层MCP (Model Context Protocol)Anthropic 提出的模型-工具通信协议
服务层FastMCP + Python 3.12MCP Server 实现框架
API 层GameCloud/Skyline API内部云资源管理 API
基础设施Redis + HTTPX限流存储 + 异步 HTTP 客户端

为什么选择 MCP?

MCP (Model Context Protocol) 是 Anthropic 在 2024 年底开源的协议,它解决了一个核心问题:如何让大模型安全、标准化地调用外部工具

相比传统的 Function Calling,MCP 的优势:

  1. 协议标准化:统一的工具描述和调用规范
  2. 安全可控:内置认证、限流等安全机制
  3. 解耦清晰:工具提供方和消费方完全解耦
  4. 生态丰富:可复用的工具集,一次开发多处使用

三、MCP 工具设计与实现

工具分层设计

我们将诊断工具按功能维度进行了分层设计:

aliyun/instances/diagnostic/
├── basic/                         # 基础信息
│   ├── get_instance_detail        # 实例配置信息
│   └── get_instance_status        # 运行状态
├── monitoring/                    # 监控数据
│   └── get_monitor_data           # CPU/内存/网络/磁盘监控
├── network/                       # 网络诊断
│   ├── get_network_interfaces     # 网卡信息
│   └── check_security_group_rules # 安全组规则
├── disk/                          # 磁盘诊断
│   └── get_disks_info             # 磁盘配置
└── system/                        # 系统日志
    ├── get_console_log            # 控制台日志
    ├── get_system_events          # 系统事件
    └── create_diagnostic_report   # 诊断报告

MCP Server ≠ OpenAPI 透传

在设计 MCP 工具时,一个常见的误区是:直接把 OpenAPI 接口包装成 MCP 工具,原样透传 JSON 响应

这种做法存在严重问题:

❌ 问题一:JSON 编码导致大量无效 Token 消耗

标准 JSON 格式包含大量结构性字符({, }, ", : 等),这些对大模型理解数据毫无帮助,却会消耗宝贵的 Token 预算。

// 原始 JSON(约 180 tokens)
{"cpu_percentage": 45.2, "memory_usage": 67.8, "disk_iops_read": 1234, "disk_iops_write": 567}

// 使用 TOON 格式(约 80 tokens)
cpu_percentage:45.2 memory_usage:67.8 disk_iops_read:1234 disk_iops_write:567

本项目使用 TOON (Token-Oriented Object Notation) 格式优化 Token 消耗,在监控数据等大体量返回场景下,可节省 40-60% 的 Token。

❌ 问题二:JSON Key-Value 结构对大模型不友好

大模型在理解 JSON 字段时容易产生幻觉,尤其是单位问题:

// API 原始返回
{"bandwidth": 100, "disk_size": 500, "memory": 8}

// 大模型可能的错误理解:
// - bandwidth 是 100 Mbps?100 Kbps?100 Gbps?
// - disk_size 是 500 GB?500 MB?500 块磁盘?
// - memory 是 8 GB?8 MB?8 个内存条?

解决方案:通过两种方式显式标注单位,彻底消除歧义:

方式一:在字段名中标注单位

return {
    "internet_bandwidth(kbits/s)": item.internet_bandwidth,
    "disk_size_gb": disk.size,
    "iops_read(次/s)": item.iops_read,
    "throughput_mbps": disk.throughput,
}

方式二:在字段值中使用自然语言标注单位(推荐)

return {
    "internet_bandwidth": f"{item.internet_bandwidth} Mbps",
    "disk_size": f"{disk.size} GB",
    "memory": f"{instance.memory / 1024} GB",
    "iops_read": f"{item.iops_read} 次/秒",
    "throughput": f"{disk.throughput} MB/s",
}

第二种方式更友好——大模型可以直接将字段值用于生成报告,无需再做单位转换,减少出错可能。

❌ 问题三:API 原始数据包含大量诊断无关信息

云 API 返回的数据通常很全面,但对于诊断场景,很多字段是噪音:

# API 原始返回的实例对象包含 50+ 字段
# 但诊断只需要关注这些核心字段:

return {
    "instance_id": instance.instance_id,
    "name": instance.name,
    "instance_type": instance.instance_type,
    "region": instance.region,
    "zone": instance.zone_id,
    "private_ips": instance.private_ips,
    "public_ips": instance.public_ips,
    "os_type": instance.os,
    "cpu_topology_type": instance.cpu_topology_type,
    # 删减了:创建者、标签、计费信息、过期时间等无关字段
}

监控数据的智能预处理

监控数据是诊断的核心依据,但也是最容易让大模型"翻车"的地方。

为什么不能直接返回时序数据?

云监控 API 通常返回这样的时序数据:

[
  {"timestamp": "2024-01-15T10:00:00Z", "cpu": 45.2, "memory": 67.8},
  {"timestamp": "2024-01-15T10:01:00Z", "cpu": 48.1, "memory": 68.2},
  {"timestamp": "2024-01-15T10:02:00Z", "cpu": 52.3, "memory": 69.1},
  // ... 还有几百个数据点
]

直接返回给大模型会有严重问题:

问题一:大模型不擅长处理时序数据

大模型是基于 Transformer 架构的语言模型,它擅长理解语义,但不擅长数值计算和时序模式识别。给它一堆数据点,它可能会:

  • 错误计算平均值、最大值
  • 无法识别数据趋势(上升/下降)
  • 对异常值视而不见或过度敏感

问题二:时序数据极易引发幻觉

// 原始数据:CPU 从 45% 涨到 52%
大模型可能的错误解读:
❌ "CPU 使用率飙升,系统即将崩溃"(过度解读 7% 的波动)
❌ "CPU 平均使用率约 30%"(随机猜测)
❌ "CPU 呈现周期性波动"(无中生有)

问题三:海量数据点消耗大量 Token

30 分钟的分钟级监控数据就有 30 个点,6 个指标就是 180 个数据点。直接返回会消耗数千 Token,且大部分是无效信息。

我们的解决方案:预计算统计摘要

与其让大模型做它不擅长的事,不如在 MCP Server 层完成数据分析,只给大模型结论性信息

def analyze_metric(dataset: list[dict]):
    """将时序监控数据聚合为统计摘要"""
    df = pandas.DataFrame(dataset)
    
    # 计算统计指标(大模型不擅长的事,我们来做)
    stats = df.describe(percentiles=[0.25, 0.5, 0.75, 0.95])
    
    # 计算趋势(用线性回归拟合斜率)
    for column in df.columns:
        trend_slope, trend_desc = _evaluate_series_trend(df[column])
        stats.loc["current"] = df[column].iloc[-1]        # 当前值
        stats.loc["trend_slope"] = trend_slope            # 趋势斜率
        stats.loc["trend_desc"] = trend_desc              # 人类可读的趋势描述
    
    return stats.to_dict()

def _evaluate_series_trend(series: pandas.Series):
    """评估时序数据趋势"""
    if len(series) <= 1:
        return float("nan"), "Insufficient Data"
    
    # 线性回归拟合
    slope, _ = np.polyfit(np.arange(len(series)), series, 1)
    
    # 转化成人类语言给 LLM(消除歧义)
    if slope > 0.5:
        return slope, "Rapidly Rising (快速上升)"
    elif slope > 0.05:
        return slope, "Slowly Rising (缓慢上升)"
    elif slope < -0.5:
        return slope, "Rapidly Falling (快速下降)"
    elif slope < -0.05:
        return slope, "Slowly Falling (缓慢下降)"
    else:
        return slope, "Stable (平稳)"

预处理后的数据结构

{
  "cpu_metrics": {
    "cpu_percentage": {
      "mean": 45.2,
      "std": 3.8,
      "min": 38.1,
      "max": 56.7,
      "p95": 52.3,
      "current": 48.5,
      "trend_desc": "Stable (平稳)"
    }
  }
}

这样做的优势

对比维度原始时序数据预处理后的统计摘要
Token 消耗数千 tokens约 100 tokens
大模型理解难度高(需要数值计算)低(直接读结论)
幻觉风险高(容易误判趋势)低(趋势已预计算)
诊断准确性依赖大模型数学能力由代码保证正确性

大模型拿到预处理后的数据,可以直接输出:

"CPU 使用率平均 45.2%,P95 为 52.3%,当前 48.5%,整体趋势平稳,属于正常水平。"

而不是在一堆数据点里迷失方向。

四、Prompt 工程实践

System Prompt 核心结构

你是一位专业的云计算运维专家。请协助用户诊断实例。

## 诊断流程
1. 收集基本信息(检查实例状态)
2. 监控数据分析(CPU、磁盘、网络)
3. 综合诊断报告(健康检查、安全评估)
4. 串口日志分析(检查 OOM、Panic、Failed 等关键词)

## 诊断原则
- **数据驱动**:基于实际监控数据分析,避免主观臆断
- **可操作性**:提供具体、可执行的解决方案

## ⚠️ 注意事项
- 如果日志信息不足以判断,请直接回答"信息不足,无法确诊"

## 输出格式
### 🩺 诊断结论
[用简短的语言描述核心问题]

### 🔍 证据分析
- **现象**:[描述你看到的指标或日志]
- **根因**:[解释技术原因]

### � 修复建议
1. [建议一]
2. [建议二]

Prompt 设计心得

核心原则:提供完整的输出格式模板

大模型每次生成的内容具有一定随机性。如果不约束输出格式,同一个问题可能得到风格迥异的诊断报告。

解决方案:在 Prompt 中提供完整的输出格式模板,包括:

  • 固定的章节标题(🩺 诊断结论、🔍 证据分析、🛠 修复建议)
  • 每个章节的内容占位符
  • 明确的格式要求(结构化 Markdown)

这样可以:

  1. 降低输出不一致的概率:大模型会严格按照模板填充内容
  2. 方便前端渲染:固定结构的报告更容易做 UI 展示
  3. 提升用户体验:用户每次看到的报告格式一致,更专业

其他设计要点

设计点做法原因
诊断流程明确列出步骤引导大模型按顺序调用工具,避免遗漏
诊断原则强调"数据驱动"减少大模型主观臆断,降低幻觉
注意事项允许回答"信息不足"避免大模型在信息不足时强行给出错误结论
关键词提示列出 OOM、Panic、Failed帮助大模型在日志中快速定位问题

工具调用链示例

用户输入: i-bp1xxxxxxxxxx

Step 1: 收集基本信息
→ 调用 get_instance_status     # 检查实例状态

Step 2: 监控数据分析  
→ 调用 get_monitor_data        # 获取 CPU/磁盘/网络监控

Step 3: 综合诊断报告
→ 调用 create_diagnostic_report # 获取阿里云官方诊断

Step 4: 串口日志分析
→ 调用 get_console_log         # 获取控制台日志

Agent 综合分析,输出结构化诊断报告

五、开发心得与最佳实践

1. 工具粒度的把握

踩坑:最初我们设计了一个"一键诊断"工具,返回所有信息。结果发现:

  • Token 消耗巨大
  • 大模型信息过载,分析质量下降
  • 不够灵活,无法针对性诊断

改进:拆分为多个细粒度工具,让大模型按需调用。

2. 返回数据的预处理

踩坑:直接返回 API 原始响应,包含大量冗余字段。

改进

  • 只返回诊断相关的核心字段
  • 对时序数据做统计聚合
  • 添加人类可读的描述(如趋势判断)

3. 错误处理的优雅降级

@tool.tool(description="...")
async def get_instance_status(instance_id: str) -> dict[str, Any]:
    try:
        result = await fetch_status(instance_id)
        return result.to_dict()
    except APIError as e:
        # 返回结构化错误,而不是抛异常
        return {"error_msg": f"查询实例状态失败: {e.message}"}

这样大模型可以理解"这个工具调用失败了",并尝试其他途径。

4. 认证与安全

MCP Server 通过中间件栈处理安全问题:

# 中间件执行顺序
app.add_middleware(AuthMiddleware())      # 1. 验证 Token
app.add_middleware(RateLimitMiddleware()) # 2. 限流保护
app.add_middleware(ErrorHandlingMiddleware()) # 3. 统一错误处理

关键安全措施:

  • Token 透传到下游 API,不做存储
  • 基于用户 ID 的请求限流
  • 敏感信息不写入日志