游乐游手机版
首页/AI教程/文章详情

调用远程MCP手搓查酒店自动开浏览器智能体

时间:2026-07-25 17:14
本来想写个能自己查周边酒店、自动生成网页还能帮你打开浏览器的小 Agent,结果第一步接高德 MCP 就干沉默了——控制台啥有用信息都没吐,只甩给你一句 "网页解析失败 "。说实话,之前看别人聊 Agent、MCP,总觉得是很玄乎的新概念,好像要很复杂的架构才能做。直到自己一行一行把代码敲完,看着它一轮

本来想写个能自己查周边酒店、自动生成网页还能帮你打开浏览器的小 Agent,结果第一步接高德 MCP 就干沉默了——控制台啥有用信息都没吐,只甩给你一句"网页解析失败"。

说实话,之前看别人聊 Agent、MCP,总觉得是很玄乎的新概念,好像要很复杂的架构才能做。直到自己一行一行把代码敲完,看着它一轮一轮调用工具、写文件、开页面,跑完整整 13 轮终于把三个酒店页面摆在你面前的时候,才突然反应过来:这玩意儿本质上,就是给大模型装了双手啊。

先说说想做个什么东西

需求说起来很简单:找北京南站附近最近的 3 个酒店,拿到酒店图片,自动生成 HTML 页面存到本地,再用 Chrome 打开,每个酒店占一个标签页,页面标题改成酒店名字。

人来做这件事,无非就是打开地图搜、点详情、存图片、写 HTML、开浏览器,几分钟的事儿。但让 AI 自己完整做完,就没那么直观了——它既不会打开网页,也不会写文件,连北京南站在哪都得现查。

最开始想的是,要不自己把步骤拆好,一步步调 API 拼起来?后来一想,那写 Agent 干嘛,直接写脚本不就完了。Agent 的核心乐趣不就在于,你只说目标,它自己拆步骤、自己找工具、自己干完吗。

于是搭了个 LangChain 的架子,接了四个 MCP 服务:高德地图用来查酒店,文件系统用来写 HTML,Chrome DevTools 用来控浏览器,再加一个本地自定义的备用。

第一个大坑:以为 MCP 就是个普通 URL 接口

配置MultiServerMCPClient的时候,想当然地照着本地服务的写法,把高德 MCP 的链接直接填进去了,长这样:

'amap-mcp-server': {command: 'node',"url": "https://mcp.amap.com/mcp?key=xxx"}

一运行,直接报错:网页解析失败,可能是不支持的网页类型。

当时懵了,第一反应是 key 错了,去高德开放平台重新申请了好几个 key,挨个试,全不行。又以为是网络问题,开了袋里、换了节点,还是一样。折腾了快一个小时,突然反应过来——不对啊,本地 MCP 是用 node 命令启动一个子进程,远程 HTTP 的 MCP 怎么可能也用 command 字段?

后来翻文档才搞明白,MCP 服务分两种运行方式:一种是本地子进程模式,用command + args启动;另一种是远程 SSE/HTTP 模式,得配置transport,不能把 url 随便塞进去当命令参数。

踩坑提醒:远程 HTTP 类型的 MCP 服务,不能直接套本地子进程的配置格式。不同的 MCP 客户端对远程服务的配置方式不一样,别像这样想当然乱写。

跑起来那一刻,看着它一步步干活居然有点治愈

改完配置再跑,控制台终于开始正常输出了。盯着屏幕看它一轮一轮往下走,那种感觉特别奇妙,就像站在实习生背后看他干活,笨手笨脚但步骤都对。

第 1 轮,它先调用地理编码工具,定位北京南站的坐标;第 2 轮,用坐标调周边搜索,拉出附近的酒店列表;第 3 轮更聪明,一下子并行调用了三个酒店详情查询,同时拿地址、评分和图片;第 4 到 6 轮,连续三次调用写文件,把三个酒店的 HTML 挨个存到本地;第 7 轮查了一遍允许访问的目录,第 8 轮列了下目录文件,确认自己写进去了;第 9 到 11 轮,连续开三个浏览器新标签页,分别加载三个本地 HTML;第 12 轮切到对应页面,第 13 轮截了个图确认效果。

整整 13 轮,全程没碰一下键盘,就只在最开始说了一句需求。它自己规划步骤、自己选工具、自己处理结果、自己往下推进,直到把事情做完。

第二个大坑:工具返回值格式根本不统一

其实上面这个顺利的版本,是踩了第二个坑之后才跑通的。

最开始写工具结果处理的时候,想当然认为所有 MCP 工具返回的都是字符串,直接塞进去就行。结果跑起来之后,AI 要么在原地打转重复调用工具,要么直接开始瞎编结果,轮次跑满了也没个正经输出。

打了个断点才发现,不同 MCP 服务的返回格式根本不一样。有的直接返回字符串文本,有的返回一个对象,内容包在text字段里,还有的返回数组。文件系统那个工具返回的就是对象格式,没做处理,AI 拿到的就是空内容,自然不知道下一步该干嘛。

后来加了一层兼容判断,才算稳住:

for(const toolCall of response.tool_calls){const foundTool = tools.find(t => t.name === toolCall.name);if(foundTool){const toolResult = await foundTool.invoke(toolCall.args);let contentStr;// 别问为什么知道要写这么多判断,踩了三回坑if(typeof toolResult === 'string') {contentStr = toolResult;} else if (toolResult && toolResult.text) {contentStr = toolResult.text;}messages.push(new ToolMessage({tool_call_id: toolCall.id,content: contentStr,}));}}

说真的,这部分是目前 MCP 最闹心的地方。说是统一协议,但实际返回格式各家有各家的写法。不做兼容的话,AI 拿到空结果就会开始胡来,要么死循环调用工具,要么直接给你编造数据,坑得很。

完整可跑的代码,给你们贴出来

踩完这两个坑,整个脚本基本就能顺下来了。完整的运行代码贴在这,把环境变量里的 key、本地路径换成自己的,直接就能跑。

整个代码结构其实很直白:先初始化大模型,再初始化 MCP 客户端拉取所有可用工具,把工具绑定给模型,最后写一个循环函数——一轮一轮让 AI 思考、判断要不要调用工具、执行工具并回填结果,直到它给出最终回答或者跑满最大轮次。

import 'dotenv/config';import { MultiServerMCPClient } from '@langchain/mcp-adapters';import { ChatOpenAI } from '@langchain/openai';import chalk from 'chalk';import {HumanMessage,SystemMessage,ToolMessage} from '@langchain/core/messages';// 初始化大模型,这里用 DeepSeek,兼容 OpenAI 接口格式const model = new ChatOpenAI({modelName: process.env.DEEPSEEK_API_MODEL,apiKey: process.env.DEEPSEEK_API_KEY,temperature: 0, // 做确定性任务就拉到 0,别让它自由发挥configuration: {baseURL: process.env.DEEPSEEK_API_BASE_URL,},})// 初始化 MCP 客户端,一次性接入多个工具服务const mcpClient = new MultiServerMCPClient({mcpServers: {// 高德地图 MCP,远程服务配置别写错,在这栽过跟头'amap-mcp-server': {command: 'node',"url": "https://mcp.amap.com/mcp?key=xxx"},// 本地自定义 MCP 服务,想自己封装工具就用这个'my-mcp-server': {command: 'node',args: ['D:/workspace/xjl_ai/ai/agent_in_action/mcp-demo/src/my-mcp-server.mjs'],},// 文件系统 MCP,用来读写本地文件'filesystem': {command: 'npx',args: ['-y','@modelcontextprotocol/server-filesystem',// 只开放指定目录,别把整个盘都给出去,安全第一'D:/workspace/xjl_ai/ai/agent_in_action/remote-mcp']},// Chrome DevTools MCP,用来控制浏览器打开页面、截图'chrome-devtools': {command: 'npx',args: ['-y','chrome-devtools-mcp','--port','9222']}}});// 从 MCP 客户端拿到所有可用工具,绑定给大模型const tools = await mcpClient.getTools();console.log(tools);const modelWithTools = model.bindTools(tools);// 核心 Agent 循环函数async function runAgentWithTools(query, maxIterations = 30) {const messages = [new HumanMessage(query)];// 最多跑 30 轮,防止 AI 死循环把资源吃光for(let i=0; i< maxIterations; i++){console.log(chalk.bgGreen(`正在等待第${i+1}轮 AI 思考...`));const response = await modelWithTools.invoke(messages);messages.push(response);// 没有工具调用了,说明 AI 认为任务完成,直接返回最终结果if(!response.tool_calls || response.tool_calls.length === 0){console.log(chalk.bgGreen(`n AI 最终回复: n ${response.content}`));return response.content;}console.log(chalk.bgBlue(`工具调用:${response.tool_calls.map(tc => tc.name).join(', ')}`));// 执行每一个工具调用,把结果塞回消息历史里for(const toolCall of response.tool_calls){const foundTool = tools.find(t => t.name === toolCall.name);if(foundTool){const toolResult = await foundTool.invoke(toolCall.args);let contentStr;// 兼容不同 MCP 的返回格式,纯字符串和带 text 字段的对象都处理了if(typeof toolResult === 'string') {contentStr = toolResult;} else if (toolResult && toolResult.text) {contentStr = toolResult.text;}messages.push(new ToolMessage({tool_call_id: toolCall.id,content: contentStr,}));}}}// 跑满最大轮次还没结束,直接返回最后一条消息兜底return messages[messages.length - 1].content;}// 执行具体任务:找北京南站附近3个酒店,生成HTML并打开浏览器展示await runAgentWithTools('北京南站附近的酒店,最近的 3 个酒店,拿到酒店图片,打开浏览器,展示每个酒店的图片,每个 tab 一个 url 展示,并且在把那个页面标题改为酒店名。将生成的 HTML 文件保存在当前目录下,文件名使用 hotel1.html、hotel2.html、hotel3.html');await mcpClient.close();

运行前别忘了这几件事:

  1. 先装依赖:@langchain/mcp-adapters@langchain/openaichalkdotenv 这几个包别漏装
  2. Chrome 要先开启远程调试端口:chrome --remote-debugging-port=9222,不然浏览器控制那步会失败
  3. 高德的 key 换成你自己的,别直接用这个

最终效果出来的时候,还真有点小震撼

等所有轮次跑完,浏览器自动弹出三个标签页的时候,说实话还是有点小激动的。

每个页面都整整齐齐:酒店照片在上面,地址、评分、类型在下面,页面标题也老老实实改成了酒店名字。对应的 HTML 文件也安安稳稳躺在本地目录里,文件名就是要求的 hotel1、hotel2、hotel3。

你别觉得这件事简单。从定位地点、搜索酒店、拉取详情,到生成 HTML、写入磁盘、打开浏览器,全程没有任何人工干预。没有预设步骤,没有硬编码逻辑,所有的决策都是 AI 在每一轮思考里自己做的。

这就是 Agent 最有魅力的地方——它不是给你一个文字答案,它是真的帮你把事情做完。

聊点干货:Agent 到底是怎么工作的

折腾完这一遭,算是把 Agent 那层窗户纸捅破了。之前看各种概念总觉得云里雾里,真自己手搓一个出来,发现核心逻辑简单到离谱。

大模型本身是"无手无脚"的,它只能输出文字。你问它北京南站附近有什么酒店,它要么凭训练数据瞎编,要么告诉你"你可以去地图上搜"。它自己是做不了任何实事的。

Agent 做的事情,就是在大模型外面套了一层循环:

  1. 把当前的对话上下文丢给大模型
  2. 大模型判断:是直接回答,还是需要调用工具
  3. 如果要调用工具,程序就帮它执行,拿到返回结果
  4. 把工具结果塞回对话历史,再丢给大模型
  5. 重复上面的步骤,直到大模型说"事儿办完了"

就这么简单。所谓的 Memory 记忆、RAG 知识库、MCP 工具,全都是往这个循环里添东西。

  • Memory 就是每次循环都把历史对话带上,让它记得之前干了啥
  • RAG 就是在提问的时候,把知识库查出来的内容一起塞进去
  • MCP 就是给所有工具定了个统一格式,不用每个工具都单独写适配代码

说白了,Agent = 一个会思考的大脑 + 一堆能用的工具 + 一个循环调度的壳子。大脑负责想,工具负责干,壳子负责传话。

顺便说个性能小细节:工具可以并行调用

不知道你注意到没有,第三轮的时候,它一下子调用了三个maps_search_detail,不是一个一个来的。

这就是大模型的并行工具调用能力。同一个步骤里如果有多个互不依赖的工具,它会一次性全列出来,不用等上一个跑完再调下一个。

代码里是用 for 循环挨个执行的,其实完全可以改成Promise.all并行处理,三个工具同时跑,速度能快不少。尤其是那种一次调用五六个工具的场景,优化完体感提升特别明显。

说到 Promise 就多提两句,很多刚接触异步的朋友容易搞混。简单理解就是:

  • Promise 就是一件"正在干、还没干完"的事,有等待、成功、失败三种状态,变了就不能改了
  • Promise.all就是好几件事同时干,等所有事都成功了才一起返回结果,有一个失败就整体失败
  • await 就是等着这件事干完,再往下走代码

写 Agent 的时候,异步处理写得好不好,直接影响整个智能体的响应速度。

最后说几句掏心窝的

折腾完这一圈,最大的感受有三个。

第一,别把 Agent 想得太高大上。它不是什么强人工智能,就是"大模型 + 工具调用循环"。自己手写个几十行的 while 循环,也能做个最简单的 Agent。核心门槛不在框架,在于能不能把工具接好、把场景想清楚。

第二,MCP 是个好东西,但生态还没那么成熟。统一工具协议这个思路特别棒,理论上所有符合标准的工具都能即插即用。但实际用起来,返回格式、参数校验、远程连接,到处都是细节坑,得慢慢踩。

第三,Agent 的能力边界,其实全看工具。你给它接地图,它就能查地点;给它接文件系统,它就能读写文件;给它接浏览器,它就能操作网页;给它接数据库,它就能查数据。工具越多,它能干的事就越多。想象力比技术本身重要。

当然也别神话这玩意儿。简单任务它能给你干得漂漂亮亮,步骤一多、逻辑一复杂,很容易在某一步跑偏,然后就开始死循环。所以代码里的maxIterations一定要设,别让它无限跑下去把资源吃光。

反正这次折腾完,算是彻底搞懂 Agent 到底是怎么回事了。之前看各种文章讲概念,总觉得隔了一层,真的自己手搓一个出来,踩一遍坑,啥都通透了。

如果你也在搭自己的小 Agent,踩了什么奇奇怪怪的坑,或者有更优雅的写法,欢迎在评论区留个言。也想看看大家都是怎么玩的。

来源:https://juejin.cn/post/7663014213996609582
上一篇WorkBuddy自动化实战:TTS配音从安装到跑通全流程 下一篇Dify自定义插件实战:30分钟用FastAPI+localtunnel搭建天气查询工具
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
TalkVisions实时视频翻译应用,消除语言障碍
AI教程 · 2026-07-25

TalkVisions实时视频翻译应用,消除语言障碍

TalkVisions是一款实时视频翻译应用,能将视频中的口语实时转录为文本并翻译成用户所选语言,以字幕形式叠加在画面上,支持多语言、低延迟,还可保存录制视频,有效消除跨语言沟通障碍。

AI驱动的日历管理工具Ipso
AI教程 · 2026-07-25

AI驱动的日历管理工具Ipso

IpsoAI是一款专为专业人士及助手打造的AI日历管理工具,能够自动协调多方日程、智能草拟邮件,并通过快速安排会议、提供智能建议及自动化工作流程,显著减少琐碎操作,帮助用户高效管理时间、提升工作效率。

Spectate企业级专业高效监控与事故管理一体化平台
AI教程 · 2026-07-25

Spectate企业级专业高效监控与事故管理一体化平台

Spectate是一款高效监控和事故管理工具,能在30秒内检测故障并推送告警。它支持Slack、PagerDuty等主流集成,提供自定义状态页面和全球性能监控。系统自动更新状态并推送修复建议,帮助团队减少沟通成本,快速解决问题。

阿里云通义千问2.5大模型发布 多项能力赶超GPT-4
AI教程 · 2026-07-25

阿里云通义千问2.5大模型发布 多项能力赶超GPT-4

通义千问2 5大模型发布,多项能力宣称赶超GPT-4,中文语境下文本理解、生成、知识问答等表现优异。相比2 1版本,理解提升9%、逻辑推理提升16%、指令遵循提升19%。开源1100亿参数模型超越Llama-3-70B,获评开源最强。已服务超9万家企业,与小米、微博等达成合作。

万知个人AI工作站:一站式智能阅读创作分享平台
AI教程 · 2026-07-25

万知个人AI工作站:一站式智能阅读创作分享平台

万知是集成多种AI能力的个人工作站,支持自然语言交互、文档快速阅读与摘要生成、PPT自动设计与优化,覆盖学术研究、商务报告、写作辅助及日常问答等场景,全方位提升工作效率。