阿里云可观测2.0 MCP实践:提升开发效率的6个技巧

探索阿里云可观测2.0融合MCP的实践技巧,提升开发效率!文章分享了MCP Server设计的6个关键经验,助力打造更智能的可观测平台。

原文标题:MCP for 可观测2.0,6个让MCP开发更高效的小妙招

原文作者:阿里云开发者

冷月清谈:

本文深入探讨了如何将 MCP(模型上下文协议)应用于阿里云可观测2.0,以提升开发效率和用户体验。MCP 是一种开放协议,旨在标准化应用程序与大语言模型(LLM)之间的上下文交互,就像 USB-C 接口为设备连接外设提供了一种标准方式。文章首先简述了 MCP 的基本概念和快速上手方法,包括 MCP 主机、客户端和服务器的角色,以及如何准备 LLM API 和添加自定义 MCP Server。接着,重点介绍了如何将 MCP 能力融入阿里云可观测2.0,通过自然语言交互更好地感知系统和分析问题,并通过服务全链路分析和可观测基础信息查询的示例展示了实际效果。最后,作者分享了MCP Server设计的6个实践经验,包括接口精简化、参数默认化、精简输出、避免链式传递、模拟数据先行以及调整 temperature 值,旨在帮助开发者更高效地设计和使用 MCP Server,并避免常见的“踩坑”问题。总结部分强调了 MCP 在未来交互工具中的潜力,并讨论了其适用场景和局限性,同时展望了可观测2.0与AI结合的未来。

怜星夜思:

1、文章提到了MCP Server设计需要考虑“三岁小孩都能懂”,但在实际应用中,我们应该如何在保证易用性的同时,兼顾专业性和功能的完整性?有没有一些具体的案例或者设计原则可以参考?
2、文章强调要避免Tools和Tools之间输入输出的链式传递,但有些场景下,链式调用似乎是不可避免的。那么,如何在链式调用中尽可能地降低出错概率,保证数据的准确性和可靠性?
3、文章提到可观测2.0 + AI 的未来展望,除了 MCP 之外,还有哪些可能的发展方向?AI 在可观测性领域还能发挥哪些作用?

原文内容

阿里妹导读


可观测近年来已经成为一个关键概念,它不仅仅局限于监控,还包括了日志记录、指标收集、分布式追踪等技术手段,旨在帮助团队更好地理解系统运行状况、快速定位问题以及优化性能。可观测2.0融合 MCP,可以让用户更好地感知系统、分析问题——用自然语言开启与系统的对话!本文将分享6个设计 MCP Server 的亲身实践,帮助大家更好地融合与使用。

一、MCP 简述

MCP 是一种开放协议,用于标准化应用程序如何向 LLM 提供上下文。可以将 MCP 视为 AI 应用程序的 USB-C 端口,就像 USB-C 提供了一种将设备连接到各种外围设备和配件的标准化方式一样,MCP 提供了一种将 AI 模型连接到不同数据源和工具的标准化方式。

  • MCP 主机 (MCP Hosts)例如 Claude Desktop、IDE 或希望通过 MCP 访问数据的 AI 工具等程序;

  • MCP 客户端 (MCP Clients)与服务器保持 1:1 连接的协议客户端;

  • MCP 服务器 (MCP Servers)轻量级程序,每个程序通过标准化的模型上下文协议 (MCP) 公开特定的功能;

  • 本地数据源 (Local Data Sources)您的计算机的文件、数据库和 MCP 服务器可以安全访问的服务;

  • 远程服务 (Remote Services)可通过互联网访问的外部系统(例如,通过 API),MCP 服务器可以连接到这些系统;

1.1 快速开始

把MCP框架理解为“大模型插件”,并且你也可以很方便地编写自己的“插件”功能。

  • [必须] 首先你需要一个支持 MCP 的 Client,参考下一章节。在本文中,使用的是 Cherry Studio [1]和DeepChat Client [2]

  • [必须] 需要准备LLM API,并准备好 API - Key。在本文中,使用的是阿里云百炼[3]。如果没有API - Key,也可以使用本地化大模型(ollama)本地化相关功能。

  • [可选] 按需添加 MCP Server,参考下两章节。

  • [可选] 编写属于自己的 MCP Server。在 https://github.com/modelcontextprotocol仓库下,选择您期望的 SDK。添加自定义功能的 Tools,就可以启动 Server 供大模型调用了。

配置如下:

1.2 Awesome MCP Clients 推荐

完整列表参考:https://github.com/punkpeye/awesome-mcp-clients

对比参考:https://modelcontextprotocol.io/clients

笔者推荐:

1.Cherry Studio

2.DeepChat

3.AIaW

4.Cursor

5.Continue

1.3.Awesome MCP Servers 推荐

完整列表参考:

https://github.com/punkpeye/awesome-mcp-servers

文档实例列表:

https://modelcontextprotocol.io/examples

官方列表:

https://github.com/modelcontextprotocol/servers

按需添加即可。

二、MCP Server for 阿里云可观测2.0

2.1 可观测2.0

过去一年,我们迈向了通用可观测之路。从“监控”到“可观测”,不仅是技术升级,更是对复杂系统认知的深化。通过因果推理的三个层级(关联、干预、反事实),可观测性能够实现从被动观测到主动预测的能力提升。企业数字化需要将黑盒系统转化为白盒,通过采集、存储和分析多模态数据(如Log、Trace、Metric等),优化运营效率。

随着分布式系统和技术栈的复杂化,可观测性成为确保系统稳定性和性能的关键。然而,许多企业面临可观测数据混乱的问题,如缺乏标准化、数据存储分散、分析效率低及知识难以沉淀。为解决这些问题,我们提出了UModel——可观测数据之魂,一种通用的可观测“交互语言”。

UModel 基于本体论思想,通过 Set 和 Link 组成的图模型描述IT世界,定义了EntitySet、LogSet、MetricSet 等核心类型,以及 EntitySetLink、DataLink 等关联关系。它支持灵活扩展,引入 CommonSchema 降低使用门槛,并提供 Explorer、告警与事件集等功能提升易用性。UModel 不仅实现了数据的标准化和统一建模,还支持算法和大模型对数据的高效利用,助力构建全新的“可观测2.0”体系。

2.2 融合 MCP 能力

在可观测2.0中融合 MCP 能力,是一种很合适的尝试,一定程度上可以通过对话的方式,让用户对可观测2.0整体使用方式有更好的感知,并且可以协助用户感知系统、分析问题——只用自然语言交流。

2.3 效果展示

2.3.1 服务全链路分析

注:此案例部分接口使用虚拟数据。

特性如下:

  • 根据某服务,检索服务上下游、依赖的中间件和基础设施

  • 分析某服务的指标情况

  • 查询某服务是否存在错误请求trace id,并进一步智能分析

2.3.2 可观测基础信息能力查询

特性如下:

查询可观测2.0中多类信息:

  • 获取可用区

  • 查找workspace

  • 查询entity 和 topo能力

  • 查询 Umodel Schema 并生成图片展示

三、设计好 MCP Server 的亲身实践(血泪踩坑)

3.1 经验一:Tools 接口精简化、原子化

写在前面:MCP Server ≠ SDK API,是和人打交道的接口。

这里的“和人打交道”,是全方面的,无论是接口的理解,参数,还是返回,都要求简洁易懂。“三岁小孩都能懂”的 MCP Server Tools 才是好 Tools。

举个例子🌰:

SLS 的 getLogs SDK API 是这样的

def get_logs(
    ak: str,
    sk: str,
    region_id: str,
    project: str,
    logstore:str,
    query: str,
    from_timestamp: int,
    to_timestamp: int,
    topic: str,
    line: int,
    offset: int,
    reverse: boolean,
    powerSql: boolean
) -> list[Any]:

这个接口如果直接给到大模型作为 Tools,可以说调用的成功率为0,因为这是一个非常复杂的接口:

1.主要的瓶颈在 query 怎么写,语法是怎样的,对模型挑战非常大。这种场景适合 A2A(Agent协作),生成可靠的 SLS query 作为一个 Agent,此处不过多展开。

2.假设我们解决了query的问题,API中的很多基础信息需要多轮对话获得:aksk、region、project、logstore,实际使用上这种环境变量级别的参数重复性非常高。

3.参数中带了时间窗口,这一点需要谨慎,详情见经验二。

4.参数中带了 topic、line、offset、reverse、powerSql 这种不常用参数,使用默认值能搞定大部分请求的参数就不带。

对于这些问题,在整体的实践后,认为在阿里云上,使用 MCP Server 和用户交互的架构,可能需要做一些改变。

朴素想法:

真正适用的:

首先,这里的 MCP Server 的生命周期应该是在用户进入到 Workspace 后,才创建。阿里云提供给用户的 MCP 交互 Client + MCP Server 是非常轻量的,一个用户、一次 Workspace 访问就是一次生命周期。而此时 MCP Server 是带上 aksk、Workspace name、region id 等环境变量信息的,里面的各种 Tools 无需关心这种基础的参数了。

然后,随着用户业务的范围,可以创建若干对应模块的 Tools 以供用户按需调用。

最后,需要把常用的操作封装 MCP Tools,而不是给出一个万能的 “query” 接口。

因此,给出的 getLogs,优化过后的 MCP Server Tools 版本应该是这样的:

A2A 模式:

def get_log_tool(
    query: str,
    from_timestamp: int = Field(
        int(datetime.now().timestamp()) - 3600, description="from timestamp,unit is second"
    ),
    to_timestamp: int = Field(
        int(datetime.now().timestamp()), description="to timestamp,unit is second"
    )
) -> list[Any]:

def gen_sls_query(
    text: str,
) -> str:

功能模块化模式(仅示例):

# 获取logstore的index信息
def get_index():
  pass

获得fields字段聚合的统计信息

def get_fields_desc(
    filter: str,
    fields: List[str],
    from_timestamp: int = Field(
        int(datetime.now().timestamp()) - 3600, description=“from timestamp,unit is second”
    ),
    to_timestamp: int = Field(
        int(datetime.now().timestamp()), description=“to timestamp,unit is second”
    )
) -> list[Any]:
  # * and upstream_status >= 400 | SELECT request_uri, upstream_status, COUNT (*) AS cnt
  # FROM log
  # GROUP BY request_uri, upstream_status
  # ORDER BY cnt DESC
  # LIMIT 10

统计UV

def get_uv(
    filter: str,
    field: str
    from_timestamp: int = Field(
        int(datetime.now().timestamp()) - 3600, description=“from timestamp,unit is second”
    ),
    to_timestamp: int = Field(
        int(datetime.now().timestamp()), description=“to timestamp,unit is second”
    )
) -> list[Any]:
  # * | SELECT COUNT(DISTINCT client_ip) AS unique_visitors FROM log

这里不需要 ak、sk、project、logstore 信息的原因是,这里的 MCP 应该是在 logstore 打开的时候启动的,因此已经知道基础环境信息了。

这样的功能模块划分仅为示例,仅作说明。

3.2 经验二:Tools 接口参数默认化。慎用时间参数

案例:一个查询 SQL Query的Tools,接口是这样的:

def o11y_list_entities(
    ctx: Context,
    query: str = Field(default=None, description="query"),
    from_timestamp: int = Field(
        ..., description="from timestamp,unit is second"
    ),
    to_timestamp: int = Field(
        ..., description="to timestamp,unit is second"
    )
) -> list[str]:

为了方便大模型使用,特意增加了一个 tool 用于调用时间相关的参数:

想法很好,现实很骨感,实际使用中,经常出现传入并不合法的 from to 的情况:

导致接口直接报错,因为传入不合法的参数,这种场景甚至必须重启 client 和Server。

有时也会传入离谱的时间参数,导致后续的回答并不理想:

时间戳这里提供的是 2023-06-25 00:00:00。大模型对“现在的时间”概念模糊。提供的示例对于一个只保存30天的 LogStore 来说仍然查不出数据。

更好的方式:

def o11y_list_entities(
    ctx: Context,
    query: str = Field(default=None, description="query"),
    from_timestamp: int = Field(
        int(datetime.now().timestamp()) - 3600, description="from timestamp,unit is second"
    ),
    to_timestamp: int = Field(
        int(datetime.now().timestamp()), description="to timestamp,unit is second"
    ),
) -> list[str]:

直接给出最近1小时的默认值,极大概率都可以覆盖期望的查询,只需考虑 query 的传入,不用纠结时间的细节设置。如果返回为空,模型会考虑是否是时间问题,并反馈。


3.3 经验三:精简输出,避免过长的上下文输出

一些不好的例子:查询 workspace 的 list(返回500个workspace),查询某个 workspace 下接入的 entity 类型(返回200个)

MCP Server 和 SDK API 的功能不能完全等价看待,MCP 本质上是给人作为终端显示的,因此人眼看不过来(体感:大于20个的数量)都是意义不大的内容。每个接口的输出都应该控制 limit。可以考虑通过加入筛选检索,或者直接截断输出到10个。

除了体感之外,过长的 json response 会严重卡慢后续模型的输出速度、影响上下文理解效果。

以上是一般 json 接口的经验。在实践中,我们还会遇到这类场景:希望 Tools 返回一幅图(以 svg 或 png 的形式)。尝试了几种方案:

  • 直接返回<svg></svg> 的 data xml:client 不会正常显示。且会因为返回数据较大而卡慢速度。

  • 使用 MCP SDK 的 Image 类,返回"url": f"data:image/png;base64,{tool_result.data}":多数 client 不会正常显示,且需要模型是多模态模型。

  • 使用图片辅助服务,直接返回 markdown 的 url 格式:效果较好。

Args:
    ctx: MCP上下文,用于访问可观测客户端
    workspace_name: Workspace 名称,必须完全匹配Workspace

Returns:
   
    Schema 的 markdown 内容,直接作为你的输出打印并展示

效果:

3.4 经验四:避免 Tools 和 Tools 输入输出的链式传递

好的实践:Tool 原子能力,一个 Tool 做独立一件事。

不好的实践:Tool1 -> output -> Tool2 input -> output -> Tool3

实践中链式传递也可以做到,但有一定概率传入的参数不符合预期,且不好控制。

有一些还不错的自我修复能力是,当大模型发现参数报错的时候,会调用依赖的上一个 Tools 验证一下参数正确性。这种情况有一定概率修复,但并非完全可以。有时会执拗地认为自己没错:

在同一次问题中,通常链式传递表现尚可。如果是在同一会话中第二次询问,并不会获得第一次询问调用的接口的每一个信息,或者判断错误。

  • 实践场景一:

Tool1:查询 service 的关联上下游,返回

[
    {
        "id": "apm@apm.service:dc7495605d8395b3788f9b54defcb826",
        "type": "upstream",
        "name": "gateway",
    },
    {
        "id": "apm@apm.service:97cd7e28783143028d7e8e9c8dbce99c",
        "type": "downstream",
        "name": "checkout",
    },
]

然后其他一些分析……省略

对话上下文中,第二次询问,请帮我查询 checkout 服务的信息。

模型会直接把checkout直接传入 Tool2(参数只有一个 id,并在 Prompt 注释中说明很详细)

第二次对话必须复制

apm@apm.service:97cd7e28783143028d7e8e9c8dbce99c完整,才会正确传递参数。

  • 实践场景二:

有一个 Tool 返回 trace ids,返回了一个列表:

[
    "93b9111f425a4b6cab6548aa68d18f04",
    "e165fede431c4c178d1e7e399c92b70d",
    "ce7d4e9f74634252969adfd62b509fd6",
    "e824356ddf4f42d884abe7c6a2d55d66",
]

每一个值是 trace id。

期望的是模型使用每一个 trace id 调用分析的接口,但是模型会自己传错。

3.5 经验五:在接口实现开工之前,首先以模拟数据作为尝试

模型使用接口的方式,可能和预期不一样。接口的设计可能需要大量改动,才能让模型按照你想要的方式运作,尤其涉及 Tools 链式调用的时候,更要谨慎。

在设计 MCP 接口的时候,首先定义 Tools 接口信息,然后编写 Tools 的注释说明(引导 Prompt),接着按照预期实现的返回数据,mock 一些假数据直接返回。在交互的过程中不断调整接口信息、注释信息,直到假数据可以按照预期正常工作,最后再开始接口的开发,否则如果先实现接口,返工率极高,十分心痛。

3.6 经验六:有时模型会“盲目自信”、“假装工作”。适当调低 temperature

这个 case 里面,trace id 传错是第一个问题,之前(经验四)已经讲过。

在之后,又遇到了时间的坑。(经验二)

在发生错误之后,模型并不会提示问题,而是结合一些正确的接口输入输出数据,自己悄悄生成假模假样的数据……像不像论文里瞎编数据的无良大学生[狗头]。

这里上下游的数据也是假的,从 id 也能看出来。

这个问题调低 tempreture 可以缓解,但是并非可以完全解决,MCP 本质上还是依赖生成模型,而生成模型是否适合一些严肃严格场景的接口,这里稍稍打一个问号。

除此之外,观察到如果在同一对话上下文中多次问类似实体,但id不同的实体,模型似乎并不会真正调用接口了,而是直接生成 tools 的输入参数,建议用户去调(谁是老板??)。

还有一种场景是受上下文影响过大,会参考上次的输出,然后新实体的关联、上下游也和上次调用的实体关联、上下游一样(糊弄我是吧??

四、总结

MCP 是标准化 LLM 上下文交互的开放协议,包含主机、客户端、服务器及数据源组件,支持连接本地/远程服务。用户需配置支持 MCP 的客户端(如Cherry Studio)和 LLM API(如阿里云百炼),并可扩展官方/自定义 MCP 服务器。阿里云可观测2.0通过 UModel 统一多模态数据交互,解决系统复杂性带来的观测难题,实现从被动监控到主动预测的升级。

4.1 MCP Server 设计注意事项和思考

MCP 作为人、LLM、业务场景融合的媒介和工具,是新一代交互工具的萌芽,但仍然存在很多设计场景上的注意点。

如果判断下来都是“是”,则非常适合 MCP 场景。

一些不适合的场景,通过某种转化,可以变成适合的场景。比如股票交易系统不提供交易接口,只提供股票代码查询的能力,规避安全性问题。提供股票查询后,规避返回股票信息具体细节,而引导模型输出报告,模型会输出对这支股票的看多看空思路和依据,提升宽容性。

同时也可以发现,MCP 天生适合理解业务短平快的需求场景,如果想解决一个非常复杂的任务,MCP 接口可能还不够,需要 A2A + 更高级的协作、超长上下文的理解。

4.2 一些优秀的 MCP 应用场景

1.bing 搜索、Github搜索、网盘搜索等各类搜索引擎 MCP

理由:独立、简洁(返回 Top5 相关)、宽容(一定程度上)、鲁棒、安全。

2.高德地图 MCP 生成旅游路线

理由:独立、宽容(每次不同甚至有新鲜感)、鲁棒(有高德地图稳定的系统为支持)、安全。

3.Web 浏览器自动化,相关爬取、统计、分析

理由:独立、简洁(输出约束一下)、宽容、鲁棒。

4.FileSystem

理由:独立、简洁、宽容(理由是 linux 命令行 llm 非常懂,不太可能写错)、安全(不提供 rm 能力)

5.Redis

理由:独立、简洁、宽容(非严肃 Redis 数据库)

4.3 可观测2.0 + AI的未来展望

MCP 适用于短平快的独立接口能力,对于复杂需求,更适合 A2A 或更强大的模式。更多结合AI的能力,不仅仅有 MCP,可观测2.0 + AI,还有更多可能。

参考:

[1]https://github.com/CherryHQ/cherry-studio
[2]https://deepchat.thinkinai.xyz/

[3]https://help.aliyun.com/zh/model-studio/get-api-key

[4]https://modelcontextprotocol.io/introduction

[5]https://github.com/punkpeye/awesome-mcp-clients

[6]https://modelcontextprotocol.io/clients

[7]https://deepchat.thinkinai.xyz/

[8]https://help.aliyun.com/zh/model-studio/get-api-key

[9]https://github.com/punkpeye/awesome-mcp-servers

[10]https://modelcontextprotocol.io/examples

[11]https://github.com/modelcontextprotocol/servers

使用Elasticsearch的向量检索能力进行个性化推荐


在电商领域,个性化推荐系统是提高用户满意度与销售转化率的关键工具。本文将探讨如何利用Elasticsearch的向量检索能力,实现商品个性化推荐,助力电商平台提升用户体验和业务增长。    


点击阅读原文查看详情。


这个问题问到了点子上!链式调用确实能提高效率,但风险也高。我的建议是,在设计链式调用时,要做好以下几点:一是明确每个Tool的输入输出规范,避免数据类型不匹配;二是增加数据校验环节,确保数据的有效性;三是提供错误处理机制,当某个Tool出错时,能够及时中断调用链,并给出明确的错误提示。还可以考虑引入工作流引擎,对调用链进行统一管理和监控。

其实我觉得“三岁小孩都能懂”有点理想化了,完全不懂技术的人是不可能用好 MCP Server 的。更现实的做法是针对特定用户群体进行设计。比如,如果用户是运维人员,那就提供一些常用的运维命令;如果用户是开发人员,那就提供一些常用的调试工具。关键是了解用户的需求和痛点,然后提供有针对性的解决方案。当然,接口的易用性也是很重要的,但不能为了易用性而牺牲功能。

我认为AI 在可观测性领域最大的潜力在于“预测”。传统的监控只能告诉我们“发生了什么”,而 AI 可以预测“将要发生什么”。比如,AI 可以分析历史数据,预测未来的流量高峰,提前做好应对;可以检测潜在的安全风险,及时发出警告。这种“防患于未然”的能力,对于保障系统的稳定性和安全性至关重要。但同时也需要注意,AI 模型的准确性需要不断验证和优化,避免出现“狼来了”的误报。

别想太多,能不用链式调用就尽量别用。真要用的话,做好日志记录,方便排查问题。出了问题,重启大法好! (手动狗头)

可观测性 + AI 的想象空间太大了!我觉得除了 MCP 这种交互方式的改进,AI 还可以用于智能异常检测、根因分析、容量预测等方面。比如,AI 可以学习系统的正常运行模式,自动识别异常行为;可以分析海量的可观测数据,快速定位问题根源;可以预测未来的资源需求,提前进行扩容。甚至可以根据可观测数据,自动优化系统配置,提高性能。总之,AI 可以让可观测性变得更加智能、高效和自动化。

我觉得可以借鉴函数式编程的思想,尽量让每个Tool都成为一个“纯函数”,即给定相同的输入,总是返回相同的输出,且没有副作用。这样可以大大降低出错的概率。另外,可以采用“断言”的方式,在每个Tool的入口处对输入参数进行校验,确保参数符合预期。如果参数不合法,就直接抛出异常,避免后续的计算错误。

不如畅想一下,未来的可观测平台是不是可以像钢铁侠的贾维斯一样,通过语音交互就能完成各种复杂的运维操作?只要说一句“贾维斯,查一下今天服务器的CPU使用率”,就能立刻得到结果,岂不美哉?当然,这需要解决很多技术难题,比如语音识别、自然语言处理、知识图谱等等。但技术的发展速度总是超出我们的想象,也许这一天很快就会到来。

这个问题很有意思!我觉得关键在于分层设计。可以把MCP Server的接口分为面向最终用户的“简易模式”和面向专业开发者的“高级模式”。简易模式提供最常用的功能,参数少、易理解;高级模式则提供更细粒度的控制,参数更丰富,但需要一定的专业知识。比如文章中提到的 SL S的getLogs 接口,简易模式可以封装成“查询最近一小时error日志”的工具,而高级模式则保留原始的query参数,供专业人士灵活使用。

我理解的“三岁小孩都能懂”更多的是一种设计理念,即接口要足够直观,避免出现过于专业或晦涩的术语。具体实现上,可以采用“约定优于配置”的原则,为常用参数提供合理的默认值,减少用户的配置负担。同时,提供清晰的文档和示例,帮助用户快速上手。另外,UI 也是非常重要的一环,好的UI设计可以大大降低用户的学习成本。