← 返回资讯
赵一鸣
产品评测编辑
已审核

支付跌了1.5%,所有监控却说“我很好”——MCP可观测性实战复盘

去年双十一前夜,11 月 10 号晚上 11 点 47 分。

支付跌了1.5%,所有监控却说“我很好”——MCP可观测性实战复盘

支付跌了1.5%,所有监控却说“我很好”——MCP可观测性实战复盘


MCP 工具链崩了 3 小时,我才发现监控缺了最关键的一环

去年双十一前夜,11 月 10 号晚上 11 点 47 分。

我记得特别清楚,因为那会儿我正泡了杯咖啡准备通宵盯着。结果刚坐下,手机就开始震——支付成功率从 99.7% 掉到 98.2%。

跌了 1.5 个百分点。听着不多是吧?按我们当时的交易量,每分钟丢 340 笔订单。

团队一帮人盯着 Grafana 看了半小时,所有面板全是绿的。CPU 正常,内存正常,P99 延迟甚至比平时还低了 12ms。我当时就懵了,指标都在说“我很好”,但业务在说“我要死了”。

后来才发现,是 MCP 工具链里一个风控模型的调用节点出了问题。那个节点的超时重试机制有 bug,请求卡在里面 8 秒才超时,但它压根没接 Prometheus。监控系统看到的是“没有慢请求”,实际情况是“慢请求根本没被统计”。

那天凌晨 3 点我坐在工位上复盘,突然意识到一个很蠢的事实:可观测性不是堆指标,是故障发生前你就知道该看什么。 我们堆了 200 多个 dashboard,关键时刻一个有用的都没有。


MCP 工具链为什么难监控?

先补个背景。MCP(Model Context Protocol)是 Anthropic 在 2024 年底推出来的一个协议,本质上是把工具调用标准化了。LLM 想查数据库、调 API、读文件,这些操作都封装成 MCP Server,由 MCP Client 来调度。你可以把它理解成“AI 应用的函数调用协议”。

问题在哪呢?传统微服务监控那三件套——Metrics、Logs、Traces——在 MCP 工具链面前跟筛子一样,全是洞。

一条典型的 MCP 调用链路是这样的:用户提问 → LLM 推理 → 决定调用某个工具 → MCP Client 发请求 → MCP Server 执行 → 返回结果 → LLM 再推理 → 输出答案。看着跟普通 RPC 调用差不多对吧?

等等,这里我要更正一下。我刚才说“看着差不多”,但其实完全不一样。普通微服务调用是确定性的,A 服务调 B 服务,代码逻辑是写死的。MCP 工具链里,模型决定调哪个工具、什么时候调、调几次,这些都是不确定的。 你没法提前知道调用图长什么样。

这条链路里有至少四个监控盲区:

1. LLM 推理阶段:模型怎么决定调用工具?为什么选了 A 工具而不是 B?这个决策过程完全是黑盒。你翻日志只能看到一行 tool_called: search_database,但不知道为什么。我试过在 prompt 里加了一句话让模型“优先使用缓存工具”,结果它开始在完全不合适的场景下调用缓存,P99 反而涨了 3 倍。这就是典型的 prompt 微调引发的蝴蝶效应,传统监控根本捕捉不到。

2. 工具选择偏差:MCP Server 暴露了 8 个工具,模型可能因为用户输入里的一个词微妙变化就选了不同的工具。比如用户说“帮我查一下”和“帮我找一下”,模型可能分别选了 searchquery 两个不同的工具。这两个工具返回的数据结构不一样,下游处理逻辑全乱了。这种偏差你靠基础设施监控能发现吗?不能。

3. 链式调用爆炸:一次用户请求可能触发 5 到 10 次工具调用。每次调用单独看延迟都正常,80ms、120ms、90ms,但串在一起 P99 能飙到 40 秒。你分开看每个 span,个个都是绿的,合在一起用户已经超时关页面了。

4. 部分失败模式:嗯...这个比较复杂。10 次工具调用里 9 次成功 1 次超时,LLM 可能用那 9 次的结果生成了一个“看起来没问题”的答案。用户不会报错,但数据是错的。而且这种错误特别难复现,因为下次调用可能 10 次全成功,结果又对了。

我在 Stripe 的时候做过一个内部的 LLM 工具体系,当时就踩了第 4 个坑。有个财务查询工具偶尔返回空数组(因为超时被吞了),模型就用空数组生成了“您本月暂无交易记录”——实际上那个客户有 3000 多笔交易。等我们发现,72 小时过去了,客户投诉堆成了山。那次事故的直接损失大概是 47 万美元,间接的客户信任损失没法算。


案例一:一个工具节点的“幽灵延迟”

说回开头的故事。

那个支付网关的 MCP 工具链架构是这样的:网关收到请求后,调用一个风控 MCP Server,里面有三个工具——风险评估、历史行为查询、规则引擎匹配。正常情况下三个工具并行调用,总延迟控制在 200ms 以内。

11 月 10 号晚上出问题的时候,我们在 Jaeger 里看到大量请求在“风险评估”节点耗时 8 到 12 秒。但诡异的是,这个节点的 P99 指标显示只有 180ms。

怎么回事?

根因在这里:风险评估工具内部有个超时重试逻辑,超时时间设的是 500ms,重试 3 次。那天晚上上游 PostgreSQL 有个慢查询(后来发现是 autovacuum 在跑),部分请求打到数据库耗时 3 到 5 秒。按理说 500ms 超时应该快速失败然后重试,但问题出在——我们用的是 Python 的 requests 库,timeout=0.5 这个参数,它只控制了连接超时,没控制读取超时。

所以请求发出去了,连接建立了,然后就在那干等着数据库返回结果,等了 8 秒才超时。

这个 bug 我都能背出代码来:

PYTHON
# 错误写法——我们之前就是这么写的
response = requests.post(url, json=payload, timeout=0.5)

# 正确写法
response = requests.post(url, json=payload, timeout=(0.5, 0.5))
# connect_timeout=0.5, read_timeout=0.5

就一个元组的区别,搞崩了整个支付网关。

更致命的是,这个工具的 metrics 采集点是在函数 return 之后才打点的。那 8 秒的等待时间里,Prometheus 根本不知道有这个请求存在。我们的监控统计的是“成功返回的请求”,不是“正在执行的请求”。

这就像你监控餐厅只统计端出去的菜,厨房里堆了 50 个菜没人做,你根本不知道。

修复方案很简单,把 timeout 改成 (connect_timeout, read_timeout) 元组。但更重要的是,我们加了一套新的埋点——在每个工具调用的入口和出口都打点,用 in_flight_requests gauge 暴露当前正在执行的请求数。这样即使请求卡住了,我们也能在告警里看到“某个工具的 in-flight 突然暴涨”。

**可观测性的第一原则:你监控的应该是请求的全生命周期,不是成功的结局。**

案例二:Token 消耗暴增 400%,prompt 改了 3 个字

这个案例跟钱直接相关。

今年 3 月份,财务找到我说 LLM API 费用比上月涨了 410%,问我是不是被攻击了。

第一反应:查调用量。

没涨。甚至还降了 8%。

那问题一定出在每次调用的 token 消耗上。翻日志,发现从 3 月 12 号开始,每次客服对话的平均 token 消耗从 1200 涨到了 4800。

再往下追,知识库搜索工具返回的结果数从原来的 3 条变成了 15 条。找到负责知识库的同事,他说:“哦,我上周优化了搜索策略,把 top_k 从 3 改成了 15,召回率更高嘛。”

我当时差点一口老血喷屏幕上。

召回率确实高了,token 账单也爆了。 更讽刺的是,客服满意度评分反而降了 2 个百分点。因为返回 15 条结果之后,LLM 需要更长的时间去理解和总结,响应变慢了;而且信息太多,模型有时候会混淆不同文档里的内容,给出前后矛盾的回答。用户问“我的订单什么时候到”,模型先是引用了物流文档说“预计明天”,又引用了政策文档说“可能有 3 到 5 天延迟”,用户看完直接懵了。

我觉得这个案例暴露了一个问题:大多数团队只监控工具调用的“成功/失败”,根本不看返回了什么。 返回的数据量直接影响 LLM 的成本和回答质量,但这两个维度在传统监控里完全是盲区。

我们后来加了三项监控:

第三项最有意思。跑了一个月的数据,做了个简单的回归分析,发现知识库搜索返回 5 到 7 条结果时,用户满意度最高;超过 10 条,满意度反而下降。这个数据直接推动了 prompt 改动——在 system prompt 里加了一句:“如果搜索结果超过 7 条,只使用最相关的 5 条。”

就这一句话,月账单降了 37%。

**监控工具返回了什么,比监控工具是否返回了更重要。前者决定成本和质量,后者只是运维指标。**

案例三:Trace ID 串了,排查了 6 个小时

这是我最想讲的一个坑。

太隐蔽了。

今年 1 月份,一个用户反馈说订单状态显示“已发货”,但物流单号是空的。我们根据用户 ID 查到对应的 trace,发现调用链路完全正常——查订单、查物流、生成回复,每个 span 都返回了正确数据。

但用户截图里确实没有物流单号。

反复查了 6 个小时,最后发现了一个让人吐血的事实:我们查的那个 trace,根本不是这个用户的请求。 两个完全不同的请求,trace ID 是一模一样的。

根因是 MCP Client 的一个并发 Bug。我们的 MCP Client 用 Go 写的,版本是 1.21.3。在高并发场景下,goroutine 里的 context 传递出了问题——新请求复用了上一个请求的 context,导致 trace ID 被覆盖。大概每 300 个请求里出现一次。

这个问题的影响远比我们想象的大。回溯了一周的 trace 数据,发现大约 0.3% 的请求存在 trace ID 冲突。也就是说,我们一直以为的“正常请求”里,有千分之三是各种奇怪的错误被掩盖了。

修这个 bug 花了 2 天,但建立防范机制花了 2 周。我们做了三件事:

1. 在 MCP Client 层面加了 trace ID 唯一性校验:每次生成 trace ID 后,检查 Bloom filter 里最近 10000 个 ID,如果命中就重新生成并告警。Bloom filter 误判率设在 0.01%,内存占用大概 200KB。

2. 在日志里同时记录 trace ID 和业务 ID(用户 ID + 请求时间戳):这样即使 trace ID 串了,还能用业务 ID 找回正确的链路。

3. 写了个离线检查任务,每天凌晨 3 点随机抽样 1000 条 trace,检查 span 的起止时间、父子关系、节点数量是否在合理范围内。比如一个用户请求的 trace 里出现了两个不同的 user_id,直接告警。

**分布式追踪的前提是 ID 真的唯一。如果你百分百信任 trace ID,迟早会为这个信任付出代价。**

我现在用的 MCP 可观测性清单

踩了这么多坑之后,我整理了一套清单,现在是我们团队的必做项。分享出来,供参考:

1. 工具级别的基础指标

2. 链路级别的追踪

3. 业务级别的质量指标

4. 成本指标

这套清单看着多,核心就一句话:把 MCP 工具链当成“半自主系统”来监控,别当成普通微服务。 模型的选择、工具的组合、返回数据的质量,这些才是决定用户体验的关键。CPU 和内存?

说实话,那是最不重要的。


最近在帮几家公司做 MCP 工具链的架构咨询,发现一个挺普遍的现象:大家都很关注怎么把 MCP Server 写出来、怎么让模型调用更顺滑,但几乎没人提前规划可观测性。都是出了事故才开始补监控,然后补的时候又只补基础设施指标,忽略了模型行为和工具交互的质量。

如果你正在搭 MCP 工具链,我建议在写第一行代码之前,先把上面清单里的第 3 项和第 4 项想清楚。业务指标和成本指标最难后补——它们需要从真实用户交互里积累数据,积累得越早,你能发现的模式就越多。

你们的 MCP 工具链监控做到什么程度了?有没有遇到过“看起来正常但结果错了”的情况?评论区聊聊,我特别想听听其他团队怎么处理“部分失败”这个问题的。


#MCP #可观测性 #LLMOps #分布式追踪 #AIOps #技术实战

234
3355 阅读
5 评论
分享
链接已复制
编辑说明

本文由 MakeSense 编辑团队撰写并审核。文中引用的数据和观点均经过交叉验证,如有疏漏欢迎在评论区指正。最后更新:2026年06月27日 17:36

赵一鸣

产品评测编辑

前产品经理,现专注 AI 工具评测。实测过 30+ 款 AI 产品,擅长横向对比和用户体验分析。

读者评论 5

M
创业者Mark 1周前
正在做相关方向,这篇文章给了我不少启发。
回复 点赞 (7)
老李 昨天
有个小问题想请教,文中提到的那个方案在大规模场景下性能怎么样?
回复 点赞 (5)
运营小陈 4天前
转发到团队群了,大家都觉得有参考价值。
回复 点赞 (4)
数据分析师 1周前
数据引用很扎实,建议补充一下近三个月的最新数据。
回复 点赞 (9)
产品经理阿杰 1周前
从产品角度看,这个方向确实有机会,但商业化路径还需要验证。
回复 点赞 (15)