:Tavily 搜索接入 TaoToken 統(tǒng)一通道)
1. 為什么要在 Spring AI 1.1.2 里折騰 MCP 和 Tavily如果你正在用 Spring Boot 寫 AI 應(yīng)用大概率遇到過這種局面項目里同時接了 OpenAI、DeepSeek、通義千問好幾個模型每個模型的 Key 散落在不同的配置文件里Base URL 各不相同測試環(huán)境切到生產(chǎn)環(huán)境要改一堆東西。更麻煩的是當你想讓模型具備聯(lián)網(wǎng)搜索能力時又得單獨寫一套工具調(diào)用邏輯代碼越堆越厚。Spring AI 1.1.2 引入的 MCPModel Context Protocol支持恰好能解決這兩個痛點。MCP 是一種讓大模型與外部工具、資源交互的標準化協(xié)議你可以把它理解成AI 世界的 USB 接口——只要工具實現(xiàn)了 MCP Server任何支持 MCP Client 的框架都能即插即用。Tavily 是一個專為 AI 應(yīng)用設(shè)計的搜索 API每月有 1000 次免費額度非常適合做搜索增強問答。這篇內(nèi)容聚焦一條完整鏈路Spring Boot 3.5 Spring AI 1.1.2 通過 MCP 接入 Tavily 搜索同時把模型調(diào)用的 endpoint 統(tǒng)一指向 TaoToken 通道解決多模型 Key 分散、Base URL 切換繁瑣的問題。適合已經(jīng)寫過 Spring Boot、想快速給 AI 應(yīng)用加上聯(lián)網(wǎng)搜索能力的后端開發(fā)者。跟著做下來你會得到一份可復(fù)制的application.yml、一個 MCP 客戶端 Bean 定義以及把 endpoint 改到 TaoToken 后的連通性驗證步驟。先說清楚 MCP 的工作方式。MCP Server 把工具能力搜索、查庫、讀文件等以統(tǒng)一格式暴露出來MCP Client 負責連接 Server、拉取工具定義并在需要時轉(zhuǎn)發(fā)工具調(diào)用LLM 通過 Spring AI 的 tool-calling 能力在對話過程中自動決定是否調(diào)用工具。在 Spring AI 1.1.2 之前給模型接外部工具需要手寫Tool注解或FunctionCallback現(xiàn)在直接復(fù)用社區(qū)已有的 MCP Server配置即集成。TaoToken 在這里扮演的角色是統(tǒng)一通道。它兼容 OpenAI 協(xié)議提供模型對話、Coding Plan、API Keys 管理等能力。你不需要為每個模型單獨維護一套 Base URL 和 Key把 Spring AI 的 OpenAI Starter 指向 TaoToken 的 API 地址再通過模型 ID 區(qū)分不同模型即可。這樣 MCP 負責工具擴展TaoToken 負責模型接入兩者職責清晰。2. 前置準備依賴、版本與 TaoToken 通道配置動手之前先把版本對齊。Spring AI 1.1.2 對 Spring Boot 版本有要求建議用 3.5.x。Java 版本至少 17。MCP Server 這邊用tavily-mcp通過npx拉起所以機器上要有 Node.js建議 18 以上。先看 Maven 依賴。父工程的pom.xml里聲明版本號然后引入 Spring AI BOM 統(tǒng)一管理properties spring-ai.version1.1.2/spring-ai.version /properties dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client/artifactId version${spring-ai.version}/version /dependency /dependencies /dependencyManagementAI 框架模塊里引入實際使用的依賴。這里用 OpenAI Starter因為 TaoToken 兼容 OpenAI 協(xié)議dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId /dependency /dependenciesspring-ai-starter-mcp-client會自動引入 MCP 協(xié)議實現(xiàn)和 stdio/SSE 傳輸層不需要額外依賴。接下來是 TaoToken 通道的準備。訪問官網(wǎng) https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注冊后進入控制臺創(chuàng)建 API Key??刂婆_地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite API Keys 管理頁在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。創(chuàng)建好 Key 之后模型調(diào)用的 Base URL 統(tǒng)一用 https://taotoken.net/api 注意這個地址不加 UTM 參數(shù)。Tavily 這邊去 tavily.com 注冊登錄拿到TAVILY_API_KEY。免費額度每月 1000 次個人開發(fā)和小規(guī)模測試夠用。環(huán)境變量建議這樣組織避免 Key 硬編碼進代碼export TAOTOKEN_API_KEYsk-你的TaoToken密鑰 export TAVILY_API_KEYtvly-你的Tavily密鑰 export OPENAI_BASE_URLhttps://taotoken.net/apiWindows 下用set或者直接在 IDE 的 Run Configuration 里配。把 Key 放在環(huán)境變量里application.yml通過${}引用這樣不同環(huán)境切換只改環(huán)境變量配置文件不用動。3. 可復(fù)制配置application.yml 與 MCP 客戶端 Bean這一節(jié)是核心配置寫對了后面基本就通了。先看application.yml的完整片段spring: ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: ${OPENAI_BASE_URL} chat: options: model: gpt-4o-mini temperature: 0.7 embedding: options: model: text-embedding-3-small mcp: client: type: SYNC request-timeout: 60s initialized: true stdio: connections: tavily: command: cmd.exe args: - /c - npx - -y - tavily-mcplatest env: TAVILY_API_KEY: ${TAVILY_API_KEY}逐項解釋關(guān)鍵參數(shù)。type: SYNC表示同步模式適配傳統(tǒng) Servlet 應(yīng)用如果你的項目是全響應(yīng)式 WebFlux改成ASYNC。request-timeout: 60s是工具調(diào)用超時時間Tavily 搜索有時耗時較長默認值可能不夠。initialized: true非常重要它讓應(yīng)用啟動時立即初始化 MCP 連接并拉取工具列表如果設(shè)為false第一次調(diào)用時才初始化容易出現(xiàn)首次響應(yīng)慢或工具未生效的問題。stdio.connections.tavily定義了一個名為 tavily 的連接。command加args拼起來就是cmd.exe /c npx -y tavily-mcplatest通過 npx 拉取并運行 tavily-mcp。env里注入的TAVILY_API_KEY只對子進程可見不會暴露給模型。Linux 或 Mac 用戶把command改成npxargs改成[-y, tavily-mcplatest]即可。多個 MCP Server 直接在stdio.connections下繼續(xù)加比如同時接入文件系統(tǒng)stdio: connections: tavily: command: cmd.exe args: [/c, npx, -y, tavily-mcplatest] env: TAVILY_API_KEY: ${TAVILY_API_KEY} filesystem: command: cmd.exe args: [/c, npx, -y, anthropic/mcp-filesystemlatest, D:/docs]所有連接的工具會自動合并模型可以同時使用多個 MCP Server 提供的工具。然后是 Java 側(cè)的 Bean 定義。spring-ai-starter-mcp-client會自動完成啟動 MCP Server 子進程、拉取工具列表、把 MCP tools 轉(zhuǎn)換成 Spring AI 的ToolCallback、注冊ToolCallbackProviderBean 這幾件事。你要做的只有把ToolCallbackProvider掛到ChatClient上。先看自動配置類Configuration public class AiAutoConfiguration { Bean public ChatMemory chatMemory() { return MessageWindowChatMemory.builder() .maxMessages(20) .build(); } Bean public VectorStore vectorStore(EmbeddingModel embeddingModel) { return SimpleVectorStore.builder(embeddingModel).build(); } }核心是動態(tài)構(gòu)建ChatClient的工廠類Component RequiredArgsConstructor public class DynamicChatClientFactory { private final ChatMemory chatMemory; private final ToolCallbackProvider toolCallbackProvider; public ChatClient buildDefaultClient(ChatModel chatModel) { String systemPrompt 你是一個智能助手遇到實時信息需求時主動調(diào)用搜索工具。; return ChatClient.builder(chatModel) .defaultSystem(systemPrompt) .defaultAdvisors( MessageChatMemoryAdvisor.builder(chatMemory).build() ) .defaultToolCallbacks(toolCallbackProvider) .build(); } }關(guān)鍵就一行.defaultToolCallbacks(toolCallbackProvider)。這行代碼讓模型每次對話時都能看到所有 MCP Server 暴露的工具定義模型根據(jù)用戶問題自主決定是否調(diào)用工具工具調(diào)用的請求和響應(yīng)由 Spring AI 加 MCP Client 自動處理。ChatModel的構(gòu)建這里簡化了實際項目里你可以通過策略模式支持多個模型。用 TaoToken 通道時構(gòu)建OpenAiChatModel的配置如下OpenAiApi openAiApi OpenAiApi.builder() .baseUrl(https://taotoken.net/api) .apiKey(System.getenv(TAOTOKEN_API_KEY)) .build(); OpenAiChatOptions options OpenAiChatOptions.builder() .model(gpt-4o-mini) .temperature(0.7) .build(); ChatModel chatModel OpenAiChatModel.builder() .openAiApi(openAiApi) .defaultOptions(options) .build();換模型只改.model()里的 IDBase URL 和 Key 不用動這就是統(tǒng)一通道的價值。4. 驗證請求curl 連通性與日志斷言配置寫完別急著寫業(yè)務(wù)代碼先驗證鏈路通不通。分兩步先驗 TaoToken 通道再驗 MCP 工具是否掛載成功。第一步用 curl 直接打 TaoToken 的 API確認 Key 和 Base URL 沒問題curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o-mini, messages: [{role: user, content: 你好}], stream: false }返回里能看到choices數(shù)組和content字段說明通道正常。如果返回 401檢查 Key 是否復(fù)制完整、有沒有多余空格。如果返回local proxy failed之類的錯誤檢查 Base URL 是不是寫成了https://taotoken.net/api注意結(jié)尾不要多加/v1OpenAI Starter 會自動拼接路徑。第二步啟動 Spring Boot 應(yīng)用觀察控制臺日志。MCP 初始化成功會打印類似這樣的內(nèi)容i.m.client.transport.StdioClientTransport:106 - MCP server starting. i.m.client.transport.StdioClientTransport:137 - MCP server started如果看到MCP server started說明 tavily-mcp 子進程拉起來了。接著確認工具列表是否拉取成功可以在啟動類里加一段臨時日志Bean public CommandLineRunner logTools(ToolCallbackProvider provider) { return args - { ToolCallback[] callbacks provider.getToolCallbacks(); System.out.println(已加載 MCP 工具數(shù)量: callbacks.length); for (ToolCallback cb : callbacks) { System.out.println(工具名: cb.getToolDefinition().name()); } }; }正常應(yīng)該看到tavily_search之類的工具名。如果數(shù)量為 0說明 MCP 連接沒初始化成功回到第 5 節(jié)排查。第三步發(fā)一個真實請求測試搜索增強。寫個簡單的 ControllerRestController RequestMapping(/chat) RequiredArgsConstructor public class ChatController { private final DynamicChatClientFactory factory; private final ChatModel chatModel; GetMapping(value /stream, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxString chatStream(String message, String conversationId) { ChatClient client factory.buildDefaultClient(chatModel); return client.prompt() .advisors(a - a.param(ChatMemory.CONVERSATION_ID, conversationId)) .user(message) .stream() .content(); } }啟動后請求curl -N http://localhost:8080/chat/stream?message今天杭州天氣怎么樣conversationIdtest1模型會先判斷天氣是實時信息決定調(diào)用tavily_search工具MCP Client 通過 stdio 把搜索請求發(fā)給 tavily-mcp 子進程子進程調(diào)用 Tavily API 拿到結(jié)果結(jié)果返回給模型模型基于搜索結(jié)果生成最終回答并流式輸出。整個過程模型自主決策你不需要寫任何 if-else 判斷什么時候該搜索。日志里能看到工具調(diào)用的痕跡類似Tool execution request和Tool execution response。如果模型直接回答而沒有調(diào)用工具檢查defaultToolCallbacks是否掛上、initialized是否為true。5. 常見報錯排查401、local proxy failed、reading choices、OAuth這一節(jié)把實際踩過的坑列出來對照報錯找原因。401 Unauthorized。最常見的是 Key 問題。先確認TAOTOKEN_API_KEY環(huán)境變量在當前 shell 里能echo出來。如果用的是 IDE檢查 Run Configuration 的 Environment variables 有沒有配。還有一種情況是 Key 復(fù)制時帶了換行或空格用curl單獨測一下就能定位。TaoToken 的 Key 在 API Keys 頁面管理如果懷疑 Key 失效去 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 重新生成一個。local proxy failed。這個報錯通常出現(xiàn)在 Base URL 配置不對的時候。檢查spring.ai.openai.base-url是不是https://taotoken.net/api不要寫成https://taotoken.net/api/v1OpenAI Starter 會自己拼/v1/chat/completions。另外確認網(wǎng)絡(luò)能正常訪問該地址用curl -I https://taotoken.net/api看返回狀態(tài)碼。Error reading choices。這個報錯說明請求發(fā)出去了但響應(yīng)體解析失敗。常見原因是模型 ID 寫錯了TaoToken 通道不認這個模型名。去模型對話頁面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 確認可用的模型 ID然后改spring.ai.openai.chat.options.model。還有一種可能是響應(yīng)被截斷檢查request-timeout是否太短。OAuth 相關(guān)報錯。如果你用的是需要 OAuth 的 MCP Serverstdio 模式下通常不需要 OAuth但 SSE 模式可能需要。Tavily 的 MCP Server 用 API Key 就夠了不需要 OAuth。如果看到 OAuth 報錯先確認你連的是哪個 Server是不是配置里混入了其他連接。Windows 下進程啟動失敗。npx在 Windows 下實際是.cmd腳本不能直接作為command啟動必須通過cmd.exe /c npx ...。報錯通常是Cannot run program npx。按第 3 節(jié)的配置寫就沒問題。工具列表為空。檢查initialized是否為true。如果設(shè)為false第一次調(diào)用時才初始化啟動日志里看不到工具數(shù)量。另外確認 Node.js 和 npx 可用node -v npx -v版本建議 18 以上。如果 npx 拉取 tavily-mcp 很慢可以先用npx -y tavily-mcplatest手動跑一次把包緩存下來。SYNC 還是 ASYNC。項目里同時用了spring-boot-starter-webServlet就選SYNC純 WebFlux 響應(yīng)式應(yīng)用選ASYNC混合使用比如引入 webflux 做流式但主體是 Servlet也選SYNC。選錯了會出現(xiàn)工具調(diào)用阻塞或響應(yīng)異常。工具調(diào)用超時。Tavily 搜索偶爾慢默認超時可能不夠。設(shè)request-timeout: 60s或更大。如果還是超時檢查網(wǎng)絡(luò)到 Tavily API 的連通性。排查的時候有個技巧把 Spring AI 和 MCP 的日志級別調(diào)成 DEBUG能看到完整的請求響應(yīng)過程logging: level: org.springframework.ai: DEBUG io.modelcontextprotocol: DEBUG這樣工具調(diào)用的入?yún)⒑统鰠⒍紩虺鰜矶ㄎ粏栴}快很多。6. 把通道固定下來長期編碼與 Agent 場景的接入建議配置跑通之后建議把 TaoToken 通道的接入方式固定成項目規(guī)范避免每個開發(fā)者各寫一套。核心原則是 Base URL 和 Key 走環(huán)境變量模型 ID 走配置中心或數(shù)據(jù)庫代碼里只讀不寫死。對于長期做編碼輔助或 Agent 開發(fā)的場景可以考慮用 Coding Plan。它面向持續(xù)性的編碼任務(wù)和 Agent 調(diào)用在額度管理和通道穩(wěn)定性上比按次調(diào)用更合適。具體可以看 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。如果你的場景是偶爾驗證模型效果用模型對話頁面就夠了如果是接入到 CI 或自動化流程里Coding Plan 更省心。接入文檔在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各語言的調(diào)用示例和參數(shù)說明。Claude Code 相關(guān)的接入可以參考 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite 如果你用 Claude Code 做開發(fā)這個頁面有專門的配置說明?;氐?Spring AI 這邊有幾個工程化建議。第一把ChatModel的構(gòu)建封裝成工廠模型 ID 從配置讀取這樣換模型不用改代碼。第二MCP 連接配置放在application.yml里但 Key 用環(huán)境變量注入不要把 Key 提交到 Git。第三給工具調(diào)用加監(jiān)控記錄每次調(diào)用的工具名、耗時、是否成功方便排查線上問題。第四request-timeout根據(jù)實際工具調(diào)整搜索類工具給足時間本地文件類工具可以短一些。最后說一個實際經(jīng)驗MCP 工具掛載后模型的決策質(zhì)量跟 system prompt 有關(guān)系。如果發(fā)現(xiàn)模型該搜索的時候不搜索可以在 system prompt 里明確寫遇到實時信息、新聞、天氣、股價等問題時優(yōu)先調(diào)用搜索工具。如果發(fā)現(xiàn)模型濫用搜索就加一句對于常識性問題直接回答不需要搜索。這個平衡需要根據(jù)你的業(yè)務(wù)場景調(diào)。整套鏈路跑通后你得到的是一個可擴展的架構(gòu)MCP 負責工具生態(tài)想加新工具就加一個 connectionTaoToken 負責模型通道想換模型就改一個 ID。兩者解耦維護成本低。