建MCP Server:讓LLM實(shí)時(shí)搜索與抓取網(wǎng)頁(yè))
LLM 最尷尬的時(shí)刻就是你問它一件上周剛發(fā)生的事它卻一本正經(jīng)地對(duì)你說“我的知識(shí)有截止日期”。模型再聰明本質(zhì)上也只是一本訓(xùn)練期定稿的“百科全書”它永遠(yuǎn)不知道訓(xùn)練集之外的世界發(fā)生了什么。這幾年大家一直在喊“給 AI 裝上眼睛和手”而 MCP 谷歌搜索與網(wǎng)頁(yè)爬蟲 Server就是讓模型第一時(shí)間獲得最新全球資訊的最快路徑之一。這篇文章我不打算念概念直接帶你從零造兩個(gè) MCP Server一個(gè)負(fù)責(zé)實(shí)時(shí)谷歌搜索一個(gè)負(fù)責(zé)把任意網(wǎng)頁(yè)抓成干凈的正文文本。兩個(gè)服務(wù)搭完后接入支持 MCP 的客戶端你就能親眼看到原本“活在過去”的模型開始主動(dòng)上網(wǎng)查資料、翻新聞、讀文章然后給你交出一份帶時(shí)效性的結(jié)論。適合正在做 LLM 應(yīng)用、AI 智能體或者單純想讓個(gè)人助手不再“斷網(wǎng)”的朋友。1. MCP 到底是什么為什么模型需要“感官”而不只是“記憶”1.1 一句話理解 Model Context ProtocolMCPModel Context Protocol是近年由頭部 AI 研究機(jī)構(gòu)牽頭推動(dòng)的一種開放協(xié)議說穿了就一句話給模型規(guī)定了一套標(biāo)準(zhǔn)方法讓它能調(diào)用外部工具和數(shù)據(jù)源。在 MCP 之前如果你想讓自己的模型會(huì)搜索只能自己寫代碼而且每接一個(gè)能力就要定制一套接口尤其在客戶端、服務(wù)端、模型三方之間來(lái)回傳數(shù)據(jù)效率很低結(jié)構(gòu)也亂。有了 MCP 之后架構(gòu)就變成了標(biāo)準(zhǔn)的三件套宿主客戶端比如你正在用的 AI 助手界面、MCP Server實(shí)現(xiàn)具體能力比如搜索、爬蟲、讀寫數(shù)據(jù)庫(kù)、以及模型本身。Server 把能力通過統(tǒng)一的工具接口暴露出來(lái)模型在需要時(shí)會(huì)自動(dòng)“申請(qǐng)調(diào)用”這些工具拿到結(jié)果后再繼續(xù)推理。我把這個(gè)關(guān)系類比成一個(gè)偵探團(tuán)隊(duì)模型是那個(gè)很聰明但一直坐在小黑屋里的偵探MCP Server 是他伸到外面的手和眼。偵探本人不需要知道世界怎么運(yùn)轉(zhuǎn)他只需要說出“我需要今天的新聞”旁邊有人幫他拿來(lái)報(bào)紙。這個(gè)“送報(bào)紙”的動(dòng)作就是 MCP Server 干的事。1.2 為什么第一批要做的 Server 通常是搜索和爬蟲看過很多團(tuán)隊(duì)的實(shí)踐第一批接入的 MCP Server 往往不是花哨的數(shù)據(jù)庫(kù)連接器而是最樸素的兩樣搜索和網(wǎng)頁(yè)抓取。原因很現(xiàn)實(shí)——模型日常遇到的大部分“知識(shí)盲區(qū)”問題本質(zhì)都是“信息過時(shí)”或“信息缺失”這兩個(gè)工具正好能對(duì)癥下藥。比如你讓模型幫你調(diào)研某款新上市的硬件產(chǎn)品它的腦子只知道舊型號(hào)但只要給它一次搜索能力它就能先找到目標(biāo)商品的評(píng)測(cè)文章鏈接再調(diào)用爬蟲 Server 把正文全文抓回來(lái)最后完成總結(jié)。整個(gè)鏈路很絲滑因?yàn)樗阉鹘鉀Q“哪里有信息”的問題爬蟲解決“如何把信息完整取回來(lái)”的問題模型則負(fù)責(zé)把信息變成答案。1.3 “接了搜索就不幻覺了”這話只說對(duì)了一半很多朋友以為只要給模型接上搜索幻覺就自動(dòng)清零。實(shí)際用過你就會(huì)發(fā)現(xiàn)搜索返回的摘要本身就可能是舊聞抓回來(lái)的網(wǎng)頁(yè)也可能被廣告和導(dǎo)航欄淹沒真正重要的內(nèi)容也許被埋在最底下。MCP Server 不是魔法它只是把工具遞到模型手里用得好不好很大程度取決于你的工具設(shè)計(jì)、描述質(zhì)量以及返回內(nèi)容的清洗程度。這也是為什么我堅(jiān)持要親手寫 Server而不是直接找一個(gè)別人封裝好的包就完事。自己的 Server你可以精確控制返回格式、內(nèi)容長(zhǎng)度、過濾規(guī)則也更容易排查“模型為什么沒調(diào)用”這類氣死人的問題。2. 開工前的準(zhǔn)備技術(shù)棧選型與方案權(quán)衡2.1 Python 還是 TypeScript我建議這么選MCP 官方支持 Python 和 TypeScript 兩套 SDK功能上基本對(duì)等。選 Python 的理由很直接數(shù)據(jù)獲取與清洗生態(tài)實(shí)在太成熟了requests發(fā)請(qǐng)求、BeautifulSoup解析 HTML、lxml做底層解析這些都是別人踩過無(wú)數(shù)坑以后留下的輪子。如果你的團(tuán)隊(duì)本來(lái)就以 JavaScript 為主那也可以選 TypeScript寫起來(lái)同樣順手npm 上也有對(duì)應(yīng)的modelcontextprotocol/sdk。我自己開發(fā)調(diào)試這類 Server 時(shí)習(xí)慣用 Python主要是快。你想想Server 本身就是一個(gè)獨(dú)立進(jìn)程跟模型跑在哪里沒有關(guān)系用什么語(yǔ)言完全看你自己的維護(hù)能力。項(xiàng)目越小、越追求快速出活選你最有把握的語(yǔ)言就好。環(huán)境準(zhǔn)備只需要兩件事Python 3.10 以上版本然后用下面的命令把依賴裝齊。python -m venv mcp_env source mcp_env/bin/activate pip install mcp[cli] requests beautifulsoup4 lxml解釋一下每個(gè)包的作用mcp[cli]是官方 SDK 的完整安裝包帶著命令行工具方便做配置和調(diào)試requests用于發(fā) HTTP 請(qǐng)求是搜索和爬蟲兩個(gè) Server 都在用的核心依賴beautifulsoup4和lxml一對(duì)搭檔一個(gè)負(fù)責(zé)解析 HTML 標(biāo)簽一個(gè)負(fù)責(zé)提供高速解析引擎。老實(shí)說能把這幾行依賴搞清楚項(xiàng)目就已經(jīng)成了一半。2.2 搜索數(shù)據(jù)源官方 API 和“硬爬”的取舍既然標(biāo)題寫的是“谷歌搜索”那你首先要正視一個(gè)現(xiàn)實(shí)問題生產(chǎn)環(huán)境里千萬(wàn)別直接去抓搜索引擎的結(jié)果頁(yè)。我見過有些同學(xué)圖省事拿requests把搜索結(jié)果頁(yè)的 HTML 拉回來(lái)再用正則摳鏈接當(dāng)時(shí)是能用但搜索引擎的反爬機(jī)制分分鐘教你做人而且這種抓取方式在服務(wù)條款上也不規(guī)范量一大就會(huì)觸發(fā)驗(yàn)證碼得不償失。谷歌官方有 Custom Search JSON API雖然免費(fèi)額度有限每日有一定次數(shù)限制但勝在穩(wěn)定、返回結(jié)構(gòu)干凈、不會(huì)被反爬機(jī)制誤傷。實(shí)現(xiàn)思路也很簡(jiǎn)單你去申請(qǐng)一個(gè) API Key再創(chuàng)建一個(gè)自定義搜索資源搜索引擎 ID然后通過一個(gè) GET 請(qǐng)求就能拿到結(jié)構(gòu)化的搜索結(jié)果。做技術(shù)方案選型時(shí)我習(xí)慣列一張表把不同路線的優(yōu)缺點(diǎn)擺出來(lái)不然很容易被“免費(fèi)”兩個(gè)字帶偏方案優(yōu)點(diǎn)缺點(diǎn)適合場(chǎng)景谷歌官方 Custom Search API穩(wěn)定合規(guī)、結(jié)果結(jié)構(gòu)清晰、帶有摘要需申請(qǐng) Key、默認(rèn)每日免費(fèi)配額有限生產(chǎn)環(huán)境、個(gè)人/團(tuán)隊(duì)正式項(xiàng)目直接抓取搜索結(jié)果頁(yè)零成本、無(wú)需申請(qǐng) Key反爬風(fēng)險(xiǎn)高、頁(yè)面結(jié)構(gòu)隨時(shí)會(huì)變、條款不合規(guī)只建議在本地做極速技術(shù)驗(yàn)證不部署三方搜索聚合接口或開源搜索庫(kù)成本低、無(wú)需申請(qǐng)穩(wěn)定性參差不齊、部分也有請(qǐng)求限制個(gè)人學(xué)習(xí)、非關(guān)鍵用途你可能會(huì)問為什么我不直接寫“用 API”就完事因?yàn)槭袌?chǎng)上很多教程喜歡教人去爬取搜索頁(yè)看起來(lái)好像不用申請(qǐng)很省事但實(shí)際掉進(jìn)坑里的人是你自己。這個(gè)項(xiàng)目如果只是玩票怎么都行一旦你想長(zhǎng)期穩(wěn)定地給模型提供信息那就得選正規(guī)軍哪怕花兩分鐘去申請(qǐng)一個(gè) Key 也是值得的。2.3 抓取方案靜態(tài)解析還是無(wú)頭瀏覽器網(wǎng)頁(yè)爬蟲 Server 的技術(shù)路線取決于你到底要抓什么類型的網(wǎng)頁(yè)。如果目標(biāo)頁(yè)面是純服務(wù)端渲染的博客、新聞、文檔站那用requests拿 HTML再用 BeautifulSoup 提取正文又快又省資源絕大多數(shù)場(chǎng)景都?jí)蛴?。但有些站點(diǎn)是典型的 SPA 單頁(yè)應(yīng)用內(nèi)容全靠 JavaScript 動(dòng)態(tài)加載你直接發(fā)一個(gè)requests請(qǐng)求拿回來(lái)的往往是一大堆空殼 HTML里面什么都沒有。這時(shí)候就需要上無(wú)頭瀏覽器比如 Playwright讓工具先模擬打開瀏覽器、等待頁(yè)面渲染完再抓取最終內(nèi)容。雖然代價(jià)是內(nèi)存占用更高、啟動(dòng)速度更慢但面對(duì)“不渲染不出內(nèi)容”的站點(diǎn)這是唯一靠譜的方案。我做 Server 的時(shí)候會(huì)默認(rèn)先提供靜態(tài)解析方案因?yàn)?80% 的需求它能扛住同時(shí)把無(wú)頭瀏覽器的代碼單獨(dú)寫成一個(gè)可選函數(shù)真有需要再切換而不是一上來(lái)就殺雞用牛刀。3. 第一刀擼一個(gè)谷歌搜索 MCP Server3.1 申請(qǐng)密鑰和創(chuàng)建搜索引擎動(dòng)手寫代碼之前先去谷歌云控制臺(tái)開通 Custom Search JSON API拿到 API Key。然后不要忘了第二步創(chuàng)建你自己的自定義搜索引擎Programmable Search Engine并把它的唯一標(biāo)識(shí)cx 參數(shù)通常是一長(zhǎng)串字符記下來(lái)。很多初學(xué)者只拿到 Key結(jié)果發(fā)請(qǐng)求的時(shí)候缺了 cx就會(huì)收到“缺少搜索資源編號(hào)”之類的報(bào)錯(cuò)卡在原地半天。有了這兩項(xiàng)信息后建議用環(huán)境變量來(lái)管理而不是直接把密鑰寫死在代碼里。潛在風(fēng)險(xiǎn)不用我多提你只要想象一下自己把密碼寫進(jìn)代碼再推到倉(cāng)庫(kù)里的后果就行。3.2 完整代碼結(jié)構(gòu)化、帶錯(cuò)誤返回的搜索工具先看我的第一個(gè) Server 代碼。開業(yè)第一步把 SDK 里的 FastMCP 類引入進(jìn)來(lái)然后定義一個(gè)工具函數(shù)最后跑起來(lái)監(jiān)聽客戶端的調(diào)用。# google_search_server.py import os import requests from mcp.server.fastmcp import FastMCP mcp FastMCP(google-search) API_KEY os.environ.get(GOOGLE_API_KEY, ) ENGINE_ID os.environ.get(GOOGLE_SEARCH_ENGINE_ID, ) SEARCH_URL https://www.googleapis.com/customsearch/v1 mcp.tool() def search_google(query: str, num: int 5, lang: str zh-CN) - str: 在谷歌上進(jìn)行一次實(shí)時(shí)搜索返回結(jié)果的標(biāo)題、鏈接和摘要。 當(dāng)你需要獲取最近發(fā)生的事件、產(chǎn)品信息、技術(shù)方案 或者需要驗(yàn)證模型內(nèi)置知識(shí)是否過時(shí)時(shí)優(yōu)先使用這個(gè)工具。 query搜索關(guān)鍵詞num期望返回的結(jié)果數(shù)量默認(rèn) 5最大 10lang結(jié)果語(yǔ)言。 if not API_KEY or not ENGINE_ID: return 搜索服務(wù)未配置 API Key 或搜索引擎 ID請(qǐng)檢查環(huán)境變量 params { q: query, key: API_KEY, cx: ENGINE_ID, num: max(1, min(int(num), 10)), hl: lang, } try: resp requests.get(SEARCH_URL, paramsparams, timeout15) resp.raise_for_status() data resp.json() except requests.exceptions.HTTPError as e: return f搜索請(qǐng)求失敗狀態(tài)碼 {resp.status_code}可能配額已用完 except requests.exceptions.RequestException as e: return f搜索請(qǐng)求異常{e} items data.get(items, []) if not items: return 未找到相關(guān)結(jié)果建議換個(gè)關(guān)鍵詞再試 lines [] for idx, item in enumerate(items, 1): title item.get(title, ) link item.get(link, ) snippet item.get(snippet, ) lines.append(f{idx}. {title}\n鏈接{link}\n摘要{snippet}) return \n\n.join(lines) if __name__ __main__: mcp.run()這段代碼看著簡(jiǎn)單里面其實(shí)埋了幾個(gè)考慮。第一個(gè)是num參數(shù)的約束官方 API 單次返回最多 10 條所以代碼里用min卡了一下避免模型亂傳參數(shù)導(dǎo)致接口報(bào)錯(cuò)默認(rèn)設(shè)成 5是因?yàn)榻?jīng)驗(yàn)告訴我返回太多結(jié)果模型反而容易迷失重點(diǎn)5 條已經(jīng)足夠讓它交叉驗(yàn)證和判斷。第二個(gè)是所有異常都返回成字符串而不是直接拋出原因是 MCP 工具運(yùn)行在獨(dú)立進(jìn)程里拋異常只會(huì)讓模型收到一個(gè)冷冰冰的錯(cuò)誤碼它會(huì)不知所措給一句人能讀懂的提示模型就知道下一步該怎么辦。3.3 別小看工具描述它也是一種“提示詞”你可能已經(jīng)注意到我給search_google寫的 docstring 比較長(zhǎng)這也是踩過坑后的體會(huì)。MCP 工具的描述對(duì)模型來(lái)說就是一份使用說明書描述寫得太敷衍比如“搜索網(wǎng)頁(yè)”模型就不知道什么時(shí)候該用它、能解決什么問題于是會(huì)很自然地調(diào)用它那些“憑空推理”能力。描述里建議寫清楚三件事這個(gè)工具適合處理什么場(chǎng)景、參數(shù)分別是什么意思、以及有怎樣的默認(rèn)行為。你可以把描述當(dāng)成在教一個(gè)聰明的實(shí)習(xí)生用工具說明越具體他越不會(huì)亂來(lái)。模型選擇了工具之后這些描述也會(huì)影響它如何填寫參數(shù)——你多寫一句“默認(rèn) 5”模型就不會(huì)非要傳一個(gè) 10 進(jìn)來(lái)。這一步做完后可以直接在命令行里跑一下python google_search_server.py正常情況下程序會(huì)啟動(dòng)并等待輸入說明 Server 已經(jīng)在監(jiān)聽客戶端的調(diào)用了。4. 第二刀網(wǎng)頁(yè)爬蟲 Server讓模型能讀全文4.1 搜索只給線索爬蟲負(fù)責(zé)讀全文搜索 Server 有一個(gè)天然短板Google 返回的消息摘要通常只有一兩句話信息密度不夠。模型要是只依賴摘要回答你很大程度上等于拿標(biāo)題寫論文。這時(shí)候就需要爬蟲 Server 出場(chǎng)——把搜索結(jié)果里的鏈接交給它它能抓取整個(gè)頁(yè)面正文再轉(zhuǎn)成干凈文本后喂給模型讓模型基于全文內(nèi)容做總結(jié)、提取或問答。爬蟲工具的職責(zé)可以拆成四層檢查 URL 合法性和 robots 協(xié)議、發(fā)出網(wǎng)絡(luò)請(qǐng)求、清洗 HTML 提取正文、控制返回長(zhǎng)度。每一層都有講究尤其最后那層直接決定模型上下文窗口會(huì)不會(huì)被撐爆。4.2 核心實(shí)現(xiàn)清洗、解碼、截?cái)嘁粋€(gè)不能少下面是我在實(shí)際項(xiàng)目中沉淀下來(lái)的完整實(shí)現(xiàn)適合絕大多數(shù)服務(wù)端渲染頁(yè)面。# web_fetcher_server.py from urllib.parse import urlparse from urllib.robotparser import RobotFileParser import requests from bs4 import BeautifulSoup from mcp.server.fastmcp import FastMCP mcp FastMCP(web-fetcher) HEADERS { User-Agent: ( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 ) } def check_robots(url: str) - bool: rp RobotFileParser() parsed urlparse(url) robots_url f{parsed.scheme}://{parsed.netloc}/robots.txt rp.set_url(robots_url) try: rp.read() return rp.can_fetch(*, url) except Exception: return True mcp.tool() def fetch_webpage(url: str, max_length: int 6000) - str: 抓取一個(gè)網(wǎng)址的正文內(nèi)容并轉(zhuǎn)換成適合模型閱讀的純文本。 當(dāng)搜索結(jié)果中的鏈接需要進(jìn)一步讀取全文或者模型需要基于某個(gè)頁(yè)面 做總結(jié)、問答、信息抽取時(shí)使用這個(gè)工具。 url完整的 http/https 地址max_length返回文本的最大字符數(shù)默認(rèn) 6000。 if not url.startswith((http://, https://)): return 僅支持 http/https 協(xié)議請(qǐng)檢查 URL if not check_robots(url): return 該站點(diǎn) robots.txt 明確不允許抓取已跳過 try: resp requests.get(url, headersHEADERS, timeout(10, 20)) resp.raise_for_status() except requests.exceptions.RequestException as e: return f頁(yè)面請(qǐng)求失敗{e} resp.encoding resp.apparent_encoding soup BeautifulSoup(resp.text, lxml) title soup.title.string.strip() if soup.title and soup.title.string else url for tag in soup([script, style, noscript, iframe, svg]): tag.decompose() lines [ line.strip() for line in soup.get_text(\n, stripTrue).split(\n) if line.strip() ] text \n.join(lines) if len(text) max_length: text text[:max_length] \n\n[內(nèi)容過長(zhǎng)已截?cái)嘟ㄗh縮小目標(biāo)范圍或分批抓取] return f頁(yè)面標(biāo)題{title}\n頁(yè)面地址{url}\n\n{text} if __name__ __main__: mcp.run()先說編碼問題很多爬蟲拿回來(lái)亂碼十有八九是沒用對(duì)字符編碼。代碼里resp.encoding resp.apparent_encoding這行起到的作用就是從 HTML 內(nèi)容和 HTTP 頭里自動(dòng)推斷編碼而不是想當(dāng)然地用默認(rèn)編碼去解碼。這一行能幫你省掉大量“為什么中文是亂碼”的排查時(shí)間。再說清洗邏輯。for tag in soup([...]): tag.decompose()這句會(huì)把腳本、樣式、隱藏框、廣告 iframe 等對(duì)正文毫無(wú)貢獻(xiàn)的標(biāo)簽直接移除剩下的才是真正值得讀的內(nèi)容。然后我用get_text把標(biāo)簽全部剝掉只留下帶換行的純文本。最后說截?cái)唷:芏嗳瞬辉O(shè)長(zhǎng)度上限結(jié)果一個(gè)兩百 KB 的周報(bào)頁(yè)面直接把模型的上下文窗口塞滿后續(xù)對(duì)話直接廢掉。默認(rèn)給 6000 字符既保證能讀完一篇普通文章的骨架又不會(huì)讓上下文壓力太大。模型如果真想知道后續(xù)內(nèi)容它自己會(huì)想辦法換參數(shù)重新調(diào)用比我一次性塞給它一堆吞不下的大雜燴強(qiáng)得多。4.3 動(dòng)態(tài)頁(yè)面的兜底方案用 Playwright 等渲染上面這套靜態(tài)抓取方案有個(gè)邊界遇到純 JavaScript 渲染的頁(yè)面就會(huì)抓空。你requests.get之后拿到的 HTML 只有根節(jié)點(diǎn)和一段導(dǎo)流腳本那怎么辦兜底方案是引入 Playwright 的無(wú)頭瀏覽器讓頁(yè)面先完整跑一遍 JS再去取渲染完成后的內(nèi)容。需要時(shí)可以加裝依賴并運(yùn)行pip install playwright playwright install chromium然后這樣寫一個(gè)備用抓取函數(shù)from playwright.sync_api import sync_playwright def fetch_dynamic_html(url: str) - str: with sync_playwright() as p: browser p.chromium.launch(headlessTrue) page browser.new_page() page.goto(url, timeout30_000) page.wait_for_load_state(networkidle) html page.content() browser.close() return html一定要記住無(wú)頭瀏覽器是“重武器”每個(gè)頁(yè)面到渲染都要跑所有前端資源速度慢、吃內(nèi)存。我的處理方式是把動(dòng)態(tài)渲染單獨(dú)拆成一個(gè)路徑只有靜態(tài)方式返回內(nèi)容太少時(shí)才讓 Server 提示模型考慮使用動(dòng)態(tài)抓取不默認(rèn)啟用。這也呼應(yīng)了前面說的“先想清楚再動(dòng)代碼”的理念不是越激進(jìn)的方案越高級(jí)適合場(chǎng)景的方案才是好方案。5. 把兩個(gè) Server 接入模型讓 AI 真正開始“上網(wǎng)沖浪”5.1 注冊(cè)服務(wù)客戶端的配置格式MCP Server 代碼本身寫完只是完成了第 50%另 50% 是把它們登記到客戶端讓模型能看見這些工具。絕大多數(shù)支持 MCP 的客戶端都會(huì)提供一個(gè)配置文件通常叫mcp_servers.json或者類似的名字。你把自己的 Server 當(dāng)成一個(gè)個(gè)子進(jìn)程掛上去模型啟動(dòng)時(shí)就能自動(dòng)列出它們的工具列表。下面是一個(gè)通用的注冊(cè)示例請(qǐng)把路徑替換成你自己機(jī)器上兩個(gè)腳本的絕對(duì)路徑并填好環(huán)境變量{ mcpServers: { google-search: { command: python, args: [/絕對(duì)路徑/google_search_server.py], env: { GOOGLE_API_KEY: 你申請(qǐng)的 API Key, GOOGLE_SEARCH_ENGINE_ID: 你申請(qǐng)的自定義搜索引擎 ID } }, web-fetcher: { command: python, args: [/絕對(duì)路徑/web_fetcher_server.py] } } }配置里有兩個(gè)細(xì)節(jié)值得說。一是路徑盡量寫絕對(duì)路徑因?yàn)榭蛻舳诉M(jìn)程的工作目錄不一定等于你的項(xiàng)目目錄填相對(duì)路徑容易“找不到模塊”。二是環(huán)境變量直接寫在這個(gè)配置塊里而不是依賴系統(tǒng)全局變量這樣這份配置可以跟隨項(xiàng)目一起分發(fā)別人接管時(shí)不會(huì)因?yàn)樯倥淞艘粋€(gè)環(huán)境變量而崩潰。5.2 實(shí)操觀察模型會(huì)自己組合工具接入完成后新開一個(gè)對(duì)話窗口試著輸入一段“測(cè)試氣味”很濃的問題比如幫我搜索最近一周里某個(gè)領(lǐng)域的重要新聞選擇其中最有代表性的一篇讀取全文然后提煉出三個(gè)核心要點(diǎn)。你會(huì)在聊天界面的后臺(tái)記錄里看到這樣的調(diào)用鏈路模型先決定調(diào)用search_google拿到搜索結(jié)果列表然后它在一眾標(biāo)題里判斷哪一篇最值得精讀再調(diào)用fetch_webpage抓取那篇鏈接最后基于抓回的全文內(nèi)容總結(jié)出三個(gè)要點(diǎn)。注意這個(gè)過程不需要你為每種網(wǎng)站單獨(dú)寫解析邏輯它全靠搜索工具返回的鏈接和描述來(lái)決策。這就是 MCP 最有魅力的一點(diǎn)工具本身不知道模型在想什么模型也會(huì)都臨時(shí)發(fā)揮但只要接口清晰它們就可以像兩個(gè)默契的上下級(jí)一樣自動(dòng)協(xié)作。5.3 場(chǎng)景延伸懂的都懂這只是一切的開始接完搜索和爬蟲后你會(huì)發(fā)現(xiàn)模型的應(yīng)用場(chǎng)景一下子就被打開了一個(gè)量級(jí)不再局限于訓(xùn)練集那點(diǎn)“庫(kù)存”。比如每天讓模型盯幾個(gè)固定行業(yè)詞替你做一次資訊整理或者讓它對(duì)比多個(gè)網(wǎng)頁(yè)來(lái)源交叉判斷一件事是否屬實(shí)再或者讓它抓某個(gè)產(chǎn)品的官方文檔基于最新版內(nèi)容回答使用方法。這些場(chǎng)景的共同點(diǎn)都是“外部信息源模型推理”而 MCP 把兩者低成本地焊接了起來(lái)。還有人會(huì)在這個(gè)基礎(chǔ)上繼續(xù)加更多 Server比如 RSS 定時(shí)抓取、數(shù)據(jù)庫(kù)查詢、任務(wù)管理接口模型就能從“會(huì)聊天”升級(jí)成“會(huì)干活”。搜索和爬蟲只是你工具箱里最基礎(chǔ)的兩個(gè)螺絲刀但有了它們你才有底氣繼續(xù)往樓下接更多能力。6. 踩坑記錄常見問題與排查速查表6.1 一張表格解決 80% 的現(xiàn)場(chǎng)故障用這套方案半年多我把遇到的問題整理成一張速查表后續(xù)只要照著查基本都能解決?,F(xiàn)象常見原因解決思路模型始終不使用搜索工具仍用自己的知識(shí)回答工具描述太抽象或者未注冊(cè)成功檢查配置里能否看到工具把描述改得更具體寫明適合場(chǎng)景搜索返回“請(qǐng)求失敗狀態(tài)碼 403/429”API Key 有誤或免費(fèi)配額耗盡去控制臺(tái)確認(rèn)配額和余額或換個(gè) Key免費(fèi)額度有限注意請(qǐng)求頻率模型拿到搜索摘要后不再往下讀全文摘要已經(jīng)讓它覺得夠了在問題里明確要求“點(diǎn)擊鏈接閱讀全文再做總結(jié)”抓取結(jié)果亂碼編碼識(shí)別失敗或被壓縮傳輸確認(rèn)站點(diǎn)字符集必要時(shí)顯式處理 gzip/br 解壓長(zhǎng)文被截?cái)嗄P涂偨Y(jié)遺漏后半部分默認(rèn) max_length 太小或頁(yè)面太長(zhǎng)把 max_length 調(diào)到 8000甚至分兩次抓不同分段某些站返回“頁(yè)面請(qǐng)求失敗”目標(biāo)站有反爬或需要登錄換用 Playwright 無(wú)頭瀏覽器或增加 Cookie/登錄態(tài)支持服務(wù)器運(yùn)行時(shí)報(bào)模塊找不到?jīng)]有在正確的虛擬環(huán)境里跑腳本source mcp_env/bin/activate后再啟動(dòng)服務(wù)6.2 三件我在代碼里沒寫、但強(qiáng)烈建議你做的“防御武器”第一件給 Server 加一個(gè)簡(jiǎn)單緩存。模型發(fā)起的搜索請(qǐng)求經(jīng)常高度重復(fù)比如同一個(gè)問題它可能會(huì)反復(fù)確認(rèn)你可以在搜索函數(shù)外面包一層小字典key是“query num”如果命中了就直接返回緩存結(jié)果別每次都打 API。省配額也省延遲。第二件永遠(yuǎn)讓 Server 返回可讀的錯(cuò)誤信息而不是原始異常。你自己調(diào)試時(shí)看原始異常沒問題但模型只拿到異常堆棧時(shí)是猜不出意圖的你要用一句話告訴它“這次失敗可能是配額問題請(qǐng)稍后再試”。工具是給模型用的錯(cuò)誤信息也是給模型用的不是給工程師的。第三件在生產(chǎn)環(huán)境務(wù)必給模型提示引用出處。我建議代碼里不要把鏈接和摘要丟棄而是保留固定格式輸出這樣模型在回答時(shí)能順手帶上鏈接讀者一旦發(fā)現(xiàn)答案可疑可以點(diǎn)擊來(lái)源去驗(yàn)證整體可信度也會(huì)高很多。不要小看這個(gè)細(xì)節(jié)它決定了你的 AI 助手是“盲信輸出機(jī)”還是“可溯源的助手”。6.3 調(diào)試時(shí)的一個(gè)高效小習(xí)慣兩個(gè) Server 都在獨(dú)立進(jìn)程里跑調(diào)試時(shí)如果每次都要靠客戶端界面觸發(fā)效率太低。我習(xí)慣先在普通 Python 腳本里單獨(dú)調(diào)用服務(wù)里的核心函數(shù)等穩(wěn)定后再交給 MCP 框架包裝。比如寫一個(gè)簡(jiǎn)單的test.py直接調(diào)search_google和fetch_webpage先把邏輯調(diào)好再開 MCP 接入——這樣定位問題的速度能快上一倍。尤其是爬蟲環(huán)節(jié)HTML 清洗邏輯完全可以在本地反復(fù)驗(yàn)證不需要每次重啟客戶端。我現(xiàn)在個(gè)人的習(xí)慣是把 Server 里的“核心邏輯”和“工具聲明”盡量拆開核心邏輯寫成普通函數(shù)工具聲明只是薄薄一層殼。這樣既方便本地調(diào)試后續(xù)如果換了別的協(xié)議框架也只需要替換外殼核心功能代碼可以直接復(fù)用?;仡^再看這套東西我最大的感受是模型接上搜索和爬蟲之后它的形態(tài)真的會(huì)發(fā)生變化不再是你問一句它答一句的記憶復(fù)讀機(jī)而是會(huì)自己判斷“這個(gè)問題我知識(shí)不夠需要查一下”然后有步驟地完成檢索、閱讀和歸納。這種從“背教材”到“出門跑腿”的轉(zhuǎn)變才是 LLM 應(yīng)用這幾年最值得投入的方向。別嫌這兩個(gè) Server 簡(jiǎn)單它們是絕大多數(shù)智能體的地基地基打牢了后面接任何能力都會(huì)順暢得多。