☰
AI辅助H5调试:基于vConsole与MCP的日志采集方案
2026/10/7 12:32:17 网站建设 项目流程

1. 移动端调试的真实困境:为什么AI看不见你的H5日志

做过H5开发的人都有一个共同的痛:在PC浏览器上调试得好好的页面,一放到手机里就各种问题。按钮点不动、接口报错、页面白屏,而你手上只有一台手机和一个完全不知道内部发生了什么的应用。Chrome DevTools的远程调试虽然能用,但配置繁琐,需要USB连接、需要开启开发者模式、需要目标WebView支持调试协议,而且一旦涉及到跨端框架(比如某些App内嵌的WebView),远程调试经常连不上。

传统的做法是在页面里引入vConsole,一个轻量级的移动端调试面板。它能在手机屏幕上直接显示console日志、网络请求、DOM结构等信息,相当于把DevTools的核心功能搬到了手机端。这个方案确实解决了很多问题,但它有一个根本性的局限:只有人能看到,AI看不到。

这就引出了我们今天要聊的核心话题:如何让AI直接读取H5页面的日志和网络请求,从而实现真正的AI辅助debug。关键词里的vConsole、MCP、H5、debug、WebSocket,其实指向的就是这条技术链路。MCP是Model Context Protocol的缩写,它本质上是一套让AI模型能够调用外部工具、读取外部数据的标准协议。把vConsole采集到的日志和请求数据,通过WebSocket实时传输到一个MCP Server,再让AI通过MCP协议读取这些数据,就实现了"AI直接看见H5运行状态"这件事。

这套方案适合谁?适合所有在做H5开发、跨端开发、移动端WebView调试的前端工程师。不管你用的是原生H5、Vue、React还是各种小程序转H5的框架,只要你的页面跑在浏览器环境里,这套思路就能用。哪怕你之前完全没接触过MCP,只要你会写JavaScript、了解WebSocket的基本用法,就能跟着这篇文章把整套链路搭起来。

我先把整体架构说清楚,让你心里有个全景图。整个系统分三层:第一层是注入到H5页面里的采集脚本,它负责拦截console方法和网络请求,把数据通过WebSocket发出去;第二层是一个WebSocket服务端,它接收页面发来的数据并缓存;第三层是MCP Server,它把缓存的数据暴露成AI可以调用的工具接口。AI通过MCP协议调用这些工具,就能读取到实时的日志和请求信息。听起来有点绕,但实际写起来代码量并不大,核心逻辑加起来可能就两三百行。

2. 拆解vConsole的数据采集机制与WebSocket传输链路

2.1 vConsole到底采集了什么,怎么采集的

要理解整套方案,首先得搞清楚vConsole的工作原理。vConsole本质上是一个前端SDK,它在页面加载时执行,做了几件关键的事情。第一,它重写了console对象上的log、warn、error、info等方法,在调用原始方法的同时把参数格式化后存到自己的日志队列里。第二,它通过Proxy或者直接替换的方式拦截XMLHttpRequest和fetch,记录请求的URL、方法、请求头、请求体、响应状态码、响应头和响应体。第三,它还监听了window.onerror和unhandledrejection事件,捕获未处理的异常和Promise拒绝。

这些数据被vConsole收集后,默认是渲染到它自己创建的DOM面板里。但vConsole也提供了插件机制和事件系统,允许外部代码监听这些数据。我们要做的就是利用这个机制,在vConsole收集数据的同时,把数据转发到WebSocket连接上。

这里有一个关键的技术选择:是直接修改vConsole源码,还是通过它的插件API来扩展?我的建议是走插件路线。vConsole的插件系统虽然文档不算特别完善,但足够稳定,而且升级vConsole版本时不会因为源码改动导致冲突。具体做法是调用vConsole.VConsolePlugin创建一个插件实例,在插件的onReady回调里拿到vConsole的核心事件总线,然后监听console和network相关的事件。

不过实际测试下来,vConsole的事件系统对网络请求的暴露不够完整,特别是响应体的内容有时候拿不到。所以更稳妥的方案是:console日志走vConsole的事件监听,网络请求则自己单独拦截一层。这样虽然代码多了一点,但数据完整性有保障。

2.2 为什么选WebSocket而不是HTTP轮询

数据传输层选择WebSocket而不是HTTP轮询,这个决策背后有几个实际考量。HTTP轮询的问题是延迟高、无效请求多。假设你每秒轮询一次,那日志从产生到被AI读取至少有1秒延迟,而且大部分轮询请求返回的是空数据,浪费资源。WebSocket是长连接,数据产生后可以立即推送,延迟在毫秒级别。

另一个考量是双向通信的需求。虽然主要数据流是从页面到服务端,但有时候AI可能需要主动向页面发送指令,比如"清空当前日志"或者"执行一段代码"。WebSocket天然支持双向通信,HTTP轮询要实现这个就得再加一套接口。

还有一个容易被忽略的点:移动端网络环境复杂,WebSocket的断线重连机制需要认真设计。我在实际项目里遇到过手机切到后台再切回来、WebSocket连接断开但页面不知道的情况。解决方案是加心跳机制,客户端每隔一段时间发送一个ping消息,服务端回复pong,如果连续几次没收到pong就主动重连。心跳间隔建议设在15到30秒之间,太短了耗电,太长了断线发现不及时。

// WebSocket心跳与重连的核心逻辑 class LogSocket { constructor(url) { this.url = url; this.reconnectDelay = 1000; this.maxReconnectDelay = 30000; this.heartbeatInterval = 20000; this.connect(); } connect() { this.ws = new WebSocket(this.url); this.ws.onopen = () => { this.reconnectDelay = 1000; this.startHeartbeat(); }; this.ws.onmessage = (e) => { if (e.data === 'pong') { this.lastPong = Date.now(); } }; this.ws.onclose = () => { this.stopHeartbeat(); setTimeout(() => this.connect(), this.reconnectDelay); this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay); }; } startHeartbeat() { this.heartbeatTimer = setInterval(() => { if (this.ws.readyState === WebSocket.OPEN) { this.ws.send('ping'); } }, this.heartbeatInterval); } stopHeartbeat() { clearInterval(this.heartbeatTimer); } send(data) { if (this.ws.readyState === WebSocket.OPEN) { this.ws.send(JSON.stringify(data)); } } }

这段代码里的指数退避重连策略很关键。第一次断线1秒后重连,第二次2秒,第三次4秒,一直翻倍到30秒封顶。这样既保证了快速恢复,又避免了服务端挂掉时客户端疯狂重连把服务端打垮。

2.3 数据格式设计:让AI能读懂的关键

数据从页面发到服务端,格式设计直接决定了AI能不能有效理解。我的经验是不要直接把原始数据扔过去,而是要做一层结构化处理。每条日志消息应该包含这几个字段:类型(log/warn/error/network)、时间戳、页面URL、设备信息、具体内容。

网络请求的数据结构要更细致一些。除了基本的URL、method、status,还要包含请求耗时、请求体大小、响应体大小。响应体如果太大(比如超过10KB),不要全量传输,截取前几KB并标记截断。这是因为AI的上下文窗口有限,塞太多无关数据反而影响分析效果。

// 网络请求数据结构示例 { type: 'network', timestamp: 1700000000000, pageUrl: 'https://example.com/page', request: { url: '/api/user/info', method: 'GET', headers: { 'Content-Type': 'application/json' }, body: null }, response: { status: 200, headers: { 'Content-Type': 'application/json' }, body: '{"code":0,"data":{"name":"test"}}', truncated: false }, timing: { start: 1700000000000, end: 1700000000230, duration: 230 } }

时间戳用毫秒级Unix时间戳,方便排序和计算耗时。页面URL要带上,因为一个WebSocket连接可能同时接收多个页面的数据(比如你在调试一个多页应用)。设备信息包括userAgent和屏幕尺寸,有些布局问题跟设备强相关。

3. MCP Server的设计:把日志数据变成AI可调用的工具

3.1 MCP协议的核心概念与最小实现

MCP是Anthropic推出的一个开放协议,目的是让AI模型能够以标准化的方式调用外部工具和读取外部资源。你可以把它理解成AI世界的USB接口——不管什么工具,只要实现了MCP协议,AI就能用统一的方式去调用它。

MCP的核心概念有三个:Tools、Resources和Prompts。Tools是AI可以主动调用的函数,比如"获取最新日志"、"搜索包含关键词的请求"。Resources是AI可以读取的数据源,比如"当前页面的所有网络请求列表"。Prompts是预定义的提示模板,这个在我们的场景里用得比较少。

实现一个MCP Server,最直接的方式是用官方提供的SDK。TypeScript SDK和Python SDK都比较成熟。考虑到我们的WebSocket服务端大概率用Node.js写,用TypeScript SDK会更顺滑,不用跨语言通信。

一个最小的MCP Server大概长这样:

import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; const server = new Server({ name: 'h5-debug-server', version: '1.0.0' }, { capabilities: { tools: {} } }); server.setRequestHandler('tools/list', async () => ({ tools: [ { name: 'get_recent_logs', description: '获取最近的H5页面日志', inputSchema: { type: 'object', properties: { count: { type: 'number', description: '获取条数,默认50' }, level: { type: 'string', enum: ['all', 'error', 'warn'], description: '日志级别过滤' } } } } ] })); server.setRequestHandler('tools/call', async (request) => { if (request.params.name === 'get_recent_logs') { const logs = logStore.getRecent(request.params.arguments); return { content: [{ type: 'text', text: JSON.stringify(logs, null, 2) }] }; } }); const transport = new StdioServerTransport(); await server.connect(transport);

这段代码定义了一个名为get_recent_logs的工具,AI可以调用它并传入count和level参数。服务端从logStore里取出对应的日志,序列化成JSON返回。

3.2 工具设计:AI需要哪些调试能力

工具设计是整个方案里最需要动脑子的部分。设计得好,AI能高效定位问题;设计得不好,AI要么拿不到需要的数据,要么被大量无关数据淹没。

我总结了几个在实际调试中最常用的工具:

get_recent_logs:获取最近的console日志,支持按级别过滤。这个工具解决的是"页面报了什么错"的问题。

get_network_requests:获取网络请求列表,支持按URL关键词、状态码、请求方法过滤。这个工具解决的是"接口通不通、返回了什么"的问题。

get_failed_requests:专门获取失败的请求(状态码非2xx或请求超时)。这个工具是get_network_requests的快捷方式,因为排查问题时最常看的就是失败请求。

search_logs:在所有日志里搜索包含特定关键词的条目。当你知道错误信息里有某个关键词但不确定完整内容时,这个工具很有用。

get_page_info:获取当前连接的页面信息,包括URL、设备、连接时间。多页面调试时用来确认数据来源。

clear_logs:清空日志缓存。在复现一个bug之前先清空,然后操作页面,这样拿到的日志就是干净的操作序列。

这些工具的参数设计要遵循一个原则:默认值要合理,让AI不传参数也能拿到有用的数据。比如get_recent_logs不传count时默认返回50条,不传level时默认返回所有级别。AI有时候会忘记传参数,合理的默认值能避免工具调用失败。

3.3 数据缓存策略:内存队列与过期清理

WebSocket服务端收到的数据需要有地方存。最简单的方案是内存里维护一个固定长度的队列,比如保留最近1000条日志和500条网络请求。超出长度时淘汰最旧的数据。

为什么不用数据库?因为调试场景下数据是临时的,页面刷新后旧数据就没意义了。而且写数据库会增加延迟,内存队列的读写是微秒级的,完全不会成为瓶颈。

但内存队列有一个问题:如果AI在排查一个复杂问题,需要查看较早的日志,而队列已经淘汰了那些数据怎么办?我的做法是提供一个"快照"机制。当AI调用某个工具时,如果发现需要的数据已经被淘汰,就返回一个提示,建议AI先调用freeze_snapshot工具冻结当前数据。冻结后,数据不再被淘汰,直到AI主动调用release_snapshot释放。

class LogStore { constructor(maxLogs = 1000, maxRequests = 500) { this.logs = []; this.requests = []; this.maxLogs = maxLogs; this.maxRequests = maxRequests; this.frozen = false; } addLog(log) { this.logs.push(log); if (!this.frozen && this.logs.length > this.maxLogs) { this.logs.shift(); } } addRequest(req) { this.requests.push(req); if (!this.frozen && this.requests.length > this.maxRequests) { this.requests.shift(); } } getRecent(count = 50, level = 'all') { let result = this.logs; if (level !== 'all') { result = result.filter(l => l.level === level); } return result.slice(-count); } }

这个实现里frozen标志控制是否淘汰数据。冻结时队列可以无限增长,但要注意内存占用,所以冻结状态下也要设置一个硬上限,比如10000条,超过时返回警告。

4. 从零搭建:完整链路的实操步骤与关键配置

4.1 采集脚本的注入方式与兼容性处理

采集脚本要注入到H5页面里,有几种方式。最直接的是在HTML模板里加一个script标签,但这种方式在调试第三方页面或者已经上线的页面时不可行。更灵活的方式是通过构建工具注入,比如Webpack的DefinePlugin或者Vite的插件机制,在开发模式下自动注入。

如果你用的是跨端框架,比如某些App的WebView,可能需要在原生层做注入。Android可以通过WebView的evaluateJavascript方法注入,iOS可以通过WKWebView的evaluateJavaScript。这种方式的好处是不依赖页面本身的构建流程,任何页面都能注入。

注入脚本时要注意几个兼容性问题。第一,如果页面本身已经引入了vConsole,不要重复引入,检测window.vConsole是否存在即可。第二,脚本要尽早执行,最好在head里同步加载,否则可能错过页面初始化阶段的日志。第三,要考虑CSP(内容安全策略)的限制,如果页面设置了严格的CSP,内联脚本可能被阻止,这时需要把脚本放到同源的js文件里加载。

// 采集脚本的核心初始化逻辑 (function() { if (window.__H5_DEBUG_INJECTED__) return; window.__H5_DEBUG_INJECTED__ = true; const socket = new LogSocket('ws://localhost:8765'); const originalConsole = {}; ['log', 'warn', 'error', 'info'].forEach(level => { originalConsole[level] = console[level]; console[level] = function(...args) { originalConsole[level].apply(console, args); socket.send({ type: 'log', level: level, timestamp: Date.now(), pageUrl: location.href, args: args.map(formatArg) }); }; }); function formatArg(arg) { if (arg instanceof Error) { return { type: 'error', message: arg.message, stack: arg.stack }; } if (typeof arg === 'object') { try { return JSON.parse(JSON.stringify(arg)); } catch (e) { return String(arg); } } return arg; } })();

这段代码里有个细节值得注意:formatArg函数对Error对象做了特殊处理,提取message和stack。因为console.error经常用来打印错误对象,如果直接JSON.stringify会丢失stack信息。另外对普通对象做了深拷贝尝试,失败时降级为String,避免循环引用导致报错。

4.2 WebSocket服务端的搭建与消息路由

WebSocket服务端用Node.js的ws库来搭是最轻量的选择。核心逻辑是:监听连接、接收消息、解析消息类型、分发到对应的存储队列。

const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8765 }); const logStore = new LogStore(); wss.on('connection', (ws, req) => { const clientId = generateId(); console.log(`Client connected: ${clientId}`); ws.on('message', (data) => { const text = data.toString(); if (text === 'ping') { ws.send('pong'); return; } try { const msg = JSON.parse(text); if (msg.type === 'log') { logStore.addLog({ ...msg, clientId }); } else if (msg.type === 'network') { logStore.addRequest({ ...msg, clientId }); } } catch (e) { console.error('Failed to parse message:', e); } }); ws.on('close', () => { console.log(`Client disconnected: ${clientId}`); }); });

消息路由的关键是类型判断。除了log和network,还可以扩展其他类型,比如performance(性能数据)、storage(本地存储变化)等。每新增一种类型,就在LogStore里加一个对应的队列。

这里有个实际踩过的坑:WebSocket消息大小限制。默认情况下ws库对消息大小没有严格限制,但浏览器端对发送的消息大小有限制,不同浏览器不一样,一般在几MB到几十MB之间。如果某个请求的响应体特别大(比如下载了一个大文件),直接发送可能导致连接断开。所以要在客户端做截断,超过阈值(我一般设50KB)就只发前50KB并标记truncated。

4.3 MCP Server与WebSocket服务端的进程通信

MCP Server和WebSocket服务端是两个独立的进程,它们之间需要通信。最简单的方式是让它们跑在同一个Node.js进程里,MCP Server直接访问LogStore的内存数据。但MCP协议通常通过stdio通信,而WebSocket服务端需要监听端口,两者在同一个进程里并不冲突。

如果非要拆成两个进程,可以用IPC或者本地HTTP接口通信。但我的建议是不要拆,因为拆开后数据同步会引入延迟和复杂性,而调试场景对实时性要求很高。

把MCP Server和WebSocket服务端放在同一个进程里的架构是这样的:

import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { WebSocketServer } from 'ws'; const logStore = new LogStore(); // 启动WebSocket服务端 const wss = new WebSocketServer({ port: 8765 }); wss.on('connection', (ws) => { ws.on('message', (data) => { // 处理数据,写入logStore }); }); // 启动MCP Server const mcpServer = new Server({ name: 'h5-debug', version: '1.0.0' }, { capabilities: { tools: {} } }); mcpServer.setRequestHandler('tools/call', async (request) => { // 从logStore读取数据返回 }); const transport = new StdioServerTransport(); await mcpServer.connect(transport);

这个架构下,AI通过stdio与MCP Server通信,H5页面通过WebSocket与同一个进程通信,数据在内存里共享,没有额外的序列化和网络开销。

4.4 在AI客户端里配置MCP Server

MCP Server写好后,需要在AI客户端里配置才能使用。不同的AI客户端配置方式不同,但核心都是告诉客户端:有一个MCP Server,通过什么命令启动,叫什么名字。

以常见的配置文件为例,通常是一个JSON文件,里面有一个mcpServers字段:

{ "mcpServers": { "h5-debug": { "command": "node", "args": ["/path/to/mcp-server.js"], "env": { "WS_PORT": "8765" } } } }

配置好后重启AI客户端,它就会启动这个MCP Server进程,并通过stdio与之通信。你可以在AI的对话里问它"现在有哪些可用的工具",如果配置正确,它应该能列出我们定义的那些工具。

这里有个常见的坑:MCP Server启动失败但客户端不报错。原因是stdio通信下,Server的启动错误可能被吞掉。排查方法是先在终端里手动运行node /path/to/mcp-server.js,看有没有报错。如果有报错,先解决报错再配置到客户端里。

5. 实战调试场景:AI如何用这些数据定位问题

5.1 场景一:接口报错但控制台没有明显信息

这是最常见的调试场景。页面某个功能不工作,但console里没有报错,或者只有一些无关紧要的警告。传统做法是打开Network面板一个个看请求,效率很低。

有了这套系统后,你可以直接问AI:"帮我看看最近有没有失败的请求。"AI会调用get_failed_requests工具,拿到所有非2xx的请求。然后你可以进一步问:"这个500错误的请求,响应体是什么?"AI会从缓存里取出对应的响应体展示给你。

我实际遇到过一个案例:一个表单提交后页面没反应,console没有任何错误。AI通过get_failed_requests发现提交接口返回了200,但响应体里code字段是40001,表示参数校验失败。而前端代码只判断了HTTP状态码,没有判断业务状态码,所以静默失败了。这个问题如果靠人眼看Network面板,可能要翻好几个请求才能发现。

5.2 场景二:页面白屏,需要快速定位错误源头

白屏问题通常伴随一个未捕获的JS错误。AI可以调用get_recent_logs并过滤level为error,快速拿到错误信息和堆栈。但堆栈信息在移动端往往被压缩过,可读性很差。

这时候可以结合search_logs工具,搜索错误堆栈里的关键词,看看错误发生前有哪些日志输出。比如错误堆栈里有renderList这个函数名,就搜索"renderList",看看这个函数执行前后打印了什么日志。通过日志的时间序列,能还原出错误发生时的执行路径。

提示:在生产环境调试时,建议在采集脚本里加一个sourceMap解析的步骤。如果构建时生成了sourceMap,可以在服务端把压缩后的堆栈还原成源码位置,AI拿到的堆栈就是可读的。

5.3 场景三:性能问题,需要分析请求耗时分布

页面加载慢,但不确定是哪个环节慢。AI可以调用get_network_requests拿到所有请求,然后按duration排序,找出最耗时的几个请求。进一步分析这些请求的timing字段,看是DNS慢、连接慢还是响应慢。

我一般会让AI做一个汇总分析:"帮我统计一下所有请求的总耗时、平均耗时,以及耗时最长的三个请求。"AI拿到数据后能直接给出统计结果,比人一个个看快得多。

如果发现某个接口特别慢,还可以让AI对比同一个接口多次请求的耗时,看是偶发还是稳定慢。这需要get_network_requests支持按URL分组统计,我在工具实现里加了一个groupBy参数,AI可以指定按URL分组。

5.4 场景四:多页面切换时的状态追踪

在单页应用里,页面切换不会刷新浏览器,但URL会变。如果采集脚本只在页面加载时初始化一次,切换页面后的日志会混在一起。解决方案是在采集的数据里带上当前页面URL,AI分析时可以按URL过滤。

更进一步,可以监听history的pushState和replaceState事件,在页面切换时发送一个标记消息。这样AI就能知道哪些日志属于哪个页面阶段。我在实际项目里加了这个机制后,排查"从A页面跳到B页面后数据丢失"这类问题效率提升很明显。

6. 踩坑记录与稳定性优化

6.1 WebSocket连接在移动端的保活问题

移动端浏览器对后台页面的WebSocket连接管理很激进。页面切到后台几秒后,WebSocket可能被系统挂起或断开。等用户切回前台时,连接已经断了,但页面代码可能没有及时感知。

解决方案是结合visibilitychange事件。当页面从后台回到前台时,主动检查WebSocket的readyState,如果不是OPEN状态就立即重连。同时,在页面进入后台时,可以主动关闭连接以节省资源,回到前台时再重连。

document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { if (socket.ws.readyState !== WebSocket.OPEN) { socket.connect(); } } });

这个逻辑看起来简单,但实际测试中发现一个问题:快速切换前后台时可能触发多次重连,导致创建多个WebSocket连接。所以要在connect方法里加一个锁,如果正在连接中就不要重复发起。

6.2 大量日志导致的性能下降

在调试一个循环里的日志时,比如一个for循环里console.log了1000次,采集脚本会发送1000条WebSocket消息。这不仅会阻塞主线程,还可能导致服务端处理不过来。

我的优化方案是在客户端做批量发送。维护一个待发送队列,每隔100毫秒或者队列长度达到50条时,一次性打包发送。这样把1000次单独发送变成20次批量发送,性能提升很明显。

class BatchSender { constructor(socket, interval = 100, maxSize = 50) { this.socket = socket; this.queue = []; this.interval = interval; this.maxSize = maxSize; this.timer = setInterval(() => this.flush(), interval); } push(item) { this.queue.push(item); if (this.queue.length >= this.maxSize) { this.flush(); } } flush() { if (this.queue.length === 0) return; const batch = this.queue.splice(0); this.socket.send({ type: 'batch', items: batch }); } }

服务端收到batch类型的消息后,遍历items逐个处理。这样既减少了网络往返次数,又避免了单条消息过大。

6.3 敏感数据泄露的风险控制

调试数据里可能包含用户token、密码、手机号等敏感信息。如果这些数据被发送到服务端并被AI读取,存在泄露风险。虽然是在本地环境,但养成好习惯很重要。

我在采集脚本里加了一个脱敏层,对常见的敏感字段做处理。比如请求头里的Authorization字段,只保留前几位和后几位,中间用星号替代。请求体里的password、token、idCard等字段,直接替换成"[REDACTED]"。

const SENSITIVE_KEYS = ['password', 'token', 'authorization', 'idcard', 'phone']; function sanitize(obj) { if (typeof obj !== 'object' || obj === null) return obj; const result = Array.isArray(obj) ? [] : {}; for (const key in obj) { if (SENSITIVE_KEYS.some(k => key.toLowerCase().includes(k))) { result[key] = '[REDACTED]'; } else if (typeof obj[key] === 'object') { result[key] = sanitize(obj[key]); } else { result[key] = obj[key]; } } return result; }

这个脱敏逻辑要在数据发送前执行,确保敏感信息不会离开页面。虽然会增加一点CPU开销,但相比安全风险,这点开销完全值得。

6.4 MCP工具调用的超时与错误处理

AI调用MCP工具时,如果工具执行时间过长,可能会导致AI客户端超时。我们的工具大部分是内存读取,速度很快,但search_logs在日志量很大时可能变慢。

优化方案是给搜索加索引。在LogStore里维护一个倒排索引,记录每个关键词出现在哪些日志的索引位置。搜索时先查索引,再取具体日志。这样即使有10万条日志,搜索也能在毫秒级完成。

另外,所有工具都要有错误处理。如果AI传了非法参数,比如count传了负数,工具应该返回一个友好的错误信息而不是抛异常。错误信息要具体,告诉AI哪个参数有问题、应该传什么类型的值。这样AI能自我纠正并重新调用。

7. 扩展思路:从H5调试到更通用的AI可观测性

这套方案的思路其实不局限于H5调试。任何产生日志和事件的前端系统,都可以用类似的方式让AI"看见"。比如小程序的调试、Electron应用的调试、甚至Node.js服务端的调试,核心逻辑都是:采集数据、传输数据、暴露给AI。

MCP协议的价值在于它标准化了AI与外部工具的交互方式。以前要让AI读取外部数据,得针对每个AI平台写不同的插件。现在只要实现一个MCP Server,所有支持MCP的AI客户端都能用。这个生态还在快速发展,未来可能会有更多标准化的调试工具出现。

我在实际使用中最大的体会是:AI辅助debug的效率提升,关键不在于AI有多聪明,而在于你给它提供的数据有多完整、多结构化。数据质量决定了AI分析的上限。所以花时间设计好数据采集和工具接口,比反复调整AI的提示词更有效。

最后分享一个实用技巧:在让AI分析日志之前,先让它调用clear_logs清空缓存,然后你手动操作页面复现问题,再让AI读取日志。这样AI拿到的日志就是干净的操作序列,没有之前操作的干扰,分析准确率会高很多。这个习惯我坚持用了几个月,排查效率至少提升了一倍。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询