☰
Spring AI与MCP协议实现自动发帖工具实战指南
2026/9/28 14:04:44 网站建设 项目流程

最近一直在折腾 Spring AI 做 Agent 落地,发现一个特别高频的真实需求:让模型不是只聊天,而是能出去“干活”。这里说的干活,具体到内容运营场景就是自动发帖——给个主题,AI 自己查资料、写稿、排版、发布,一气呵成。把 Spring AI 和 MCP 协议结合起来,正好能把这套流程做得很顺。我用 Spring AI MCP 做自动发帖工具的完整经历,从协议选型、架构设计、核心代码到实际踩过的坑,全部梳理一遍,希望能给正在做同类工具的人省点时间。

先解释一下背景。MCP(Model Context Protocol)是一个开放协议,解决的是“大模型怎么规范、安全地调用外部工具”这层问题。你可以把它理解成 AI 世界的 USB-C 接口:不管模型是哪家的,也不管外部系统用的是 REST API 还是本地命令,只要双方都按 MCP 的标准说话,就能直接对接。Spring AI 在 1.0 版本开始把 MCP 客户端和服务端都纳入了官方支持,这件事意义不小。以前我想让 AI 调工具,得自己在代码里堆一堆 Function Calling 的胶水逻辑,现在 Spring AI 把这些全部标准化了,我能把精力放在业务逻辑本身。

自动发帖工具说白了就是三件事:内容生成、内容审核、内容发布。这三件事如果全让模型在一个函数里一口气做完,很容易出乱子——生成完直接发出去,没有 review 环节,出问题只能线下补救。用 MCP 拆开做,AI 只负责理解意图和生成内容,发布动作交给专门的 MCP 工具,整个过程可控多了:哪一步做了什么、调了哪个接口、返回了什么结果,全程有日志。这篇文章适合两类人:一类是正在用 Spring AI 做应用、想给应用外接工具的 Java 开发者;另一类是已经看过 MCP 概念介绍、但不知道在真实项目里怎么落地的人。我会按完整的实操链路来讲,从协议原理一直讲到代码实现和问题排查。

1. 为什么这篇文章选 Spring AI 而不是直接裸写

1.1 MCP 协议给自动发帖带来的核心价值

自动发帖这个需求,看起来就是一个 HTTP POST 请求的事,为什么非要绕一圈引入 MCP?这是我在项目立项时被问得最多的问题。我当时的回答是:如果只发一个平台、发固定格式的内容,直接写代码调 API 就完事了。但我要做的工具要支持多平台、要由 AI 自主决策、要预留人工审核环节,问题就变复杂了。

传统的做法是让大模型直接生成 JSON,然后代码解析 JSON 再去调平台的开放接口。这套方案的问题很明显:模型返回的 JSON 结构不稳定,要么少了字段,要么多出不属于平台允许的参数。而且一旦平台接口升级,模型不知道,你这边还得重新调 prompt 让它不改字段。MCP 的解决思路不同,它把“发帖”这个能力暴露成一个标准的工具描述,模型看到的是类似“publish_article(title, content, tags, platform)”这样一个带参数说明的调用,模型根据语义自己决定调不调、传什么值。参数校验、错误处理都在 MCP Server 端完成,模型不用关心平台 API 长什么样。

这里要给不熟悉 MCP 的读者补个基础概念:MCP 模型中,AI 应用是客户端,外部能力持有方是服务端。服务端把自己的能力声明成一系列工具,客户端负责发现这些工具,并把工具列表提供给大模型。模型在生成回复时如果需要某个能力,就会以函数调用的形式申请执行一段工具,客户端收到这个申请后去服务端实际执行,把结果返回给模型继续生成。这个过程是双向的,模型不仅能调服务,还能接收服务端发来的资源或上下文提示。

自动发帖场景里,这种架构的直接收益就是换平台成本变低。传统代码里,每接入一个新平台就要改一遍代理代码,接口签名、鉴权方式、错误处理全是硬编码。MCP 架构下,新平台只需要在 MCP Server 里多加一个工具定义,甚至可以直接配置多个 MCP Server,每个 Server 管一个平台。Agent 侧几乎不用改代码,因为模型通过 MCP 协议动态发现新工具。我后来接入第二个平台的时候,只花了一个小时就完成了,这在以前是不可想象的。

1.2 Spring AI 对 MCP 的支持到底强在哪

Spring AI 不是最早支持 MCP 的框架,但它是我用下来集成体验最顺的一个。原因有三。第一,它的 Boot Starter 做得非常成熟,引入一个依赖,配置一堆 URL,项目启动的时候 MCP 客户端会自动连接服务端并注册所有工具,不需要写任何协议层的握手代码。第二,它与 Spring Boot 的统一配置体系融合得很好,MCP 的连接配置、AI 模型的密钥配置、应用本身的配置可以放在同一份 application.yml 里,运维成本低。第三,它对 Function Calling 的抽象很干净,我在业务代码里调用 MCP 工具就像调用一个本地 Java 方法,完全不需要关心底层参数是怎么序列化成 JSON 的。

我在早期调研的时候也看过一些 Python 的方案,比如 LangChain 的 MCP 适配器。坦白讲 Python 社区在 AI 工具链上确实活跃,但对很多 Java 后端团队来说,引入一套 Python 微服务只是为做一个发帖工具,这动静太大了。Java 这边直接用 Spring AI,技术栈统一,团队成员上手快,现有 Spring Boot 服务里的用户系统、权限系统、配置中心都能直接复用。

Spring AI 在 1.0 版本里对 MCP 的支持分两条线:客户端 Starter 和服务端框架。自动发帖工具里我两种都用了。服务端这边,我可以写一个专门的 MCP Server 进程,暴露发帖工具;客户端这边,Spring AI 应用通过配置连接到这个 Server,把工具注册到模型上下文里。实际上 Spring AI 还支持在同一个 Spring Boot 进程里同时启用客户端和服务端,这在做小型工具时非常省事:写一个控制器作为入口,服务端注册发帖工具,客户端持有聊天模型,控制器收到用户指令后让模型走一遍“理解-决策-调用”流程,整个逻辑全在进程里闭环了。

2. 自动发帖工具的需求拆解与技术架构

2.1 需求拆解:从“能发帖”到“发好帖”

我在动手之前,把需求列成了清单,大概有五个层次:

  • 最基本的要求是能发帖,输入标题和正文,调用平台接口,返回文章链接。
  • 第二层是能自动生成内容,用户只给一个主题,模型自己写标题、正文和标签。
  • 第三层是发布前要有人工审核确认,不能模型写完就直接发出去,至少在测试阶段要加一道确认关卡。
  • 第四层是多平台支持,同一个内容可以发布到不同技术社区,平台之间的格式差异由后面排版逻辑处理。
  • 第五层是发布后的数据回传,比如拿到了文章 URL,要让模型知道发成功了,并且能作为后续对话的上下文。

这五个层次直接决定了 MCP 工具怎么设计。如果只做第一层,我根本不需要 MCP,写个 Service 类就完了。但要做到第三层和第五层,就需要工具调用具备状态管理能力——审核通过、发布中、发布成功这些状态要让模型感知到。MCP 的“工具调用结果返回给模型”这个机制,天然支持这种状态回传。教给模型的不只是“调一个接口”,而是“看结果再决策”。

实际操作中,我建议做自动发帖工具不要一上来就追求全自动。我第一版加了人工审核开关,默认开,后来验证稳定了才改成可选关闭。这个开关的实现很简单,在发布工具内部加一个布尔参数 preview_only,模型调用工具时可以决定是返回预览内容供人确认,还是直接执行发布。这个设计帮我在调试阶段避免了很多事故。

2.2 技术选型:一套能跑起来的组合拳

选型环节我直接给出最终确定的技术栈,并说明每层为什么这么选:

  • 基础框架:Spring Boot 3.4 + Java 21。Java 21 的虚拟线程在处理多个发帖平台并发调用时很有用。
  • AI 接入:Spring AI 1.0 版本集成的 OpenAI 兼容接口。很多大模型服务都提供 OpenAI 兼容协议,选兼容模式是为了留出换模型的余地。
  • MCP 实现:Spring AI 官方 MCP Starter。客户端和服务端一体,省去单独维护 MCP Server 进程的麻烦。
  • 调度任务:Spring 自带的 @Scheduled。定时巡检发布队列,处理失败重试。
  • 持久化:直接用 Spring Data JDBC + H2 或者 PostgreSQL。发布任务、平台凭证、内容快照都要落库。
  • 模板引擎:OpenFeign 或 RestClient 调用各平台开放 API。RestClient 是 Spring 6 的新东西,配置简单,适合这种轻量的外部调用。

有人可能会问,为什么不直接上 Python 的 FastAPI + LangChain?我的答案是:我是一个 Java 团队在干活,希望所有代码都沉淀在同一个代码仓库、走同一个发布流程。Spring AI 的生态虽然不如 Python 那么激进,但它在企业级集成上更强,配置管理、监控、日志体系都是现成的。自动发帖工具这件事,本质上是一个带一点 AI 能力的业务系统,而不是一个 AI 研究项目,选 Spring AI 比选 Python 全家桶更契合。

2.3 架构图:没有链路图的架构设计都是耍流氓

虽然我不能在这里画复杂的 Mermaid 图,但在实际项目里我是先画了一张链路图再动的手。整个架构分为三层:接入层、Agent 层、工具层。

接入层是一个 Spring Boot 控制器,接收用户的发帖指令,比如“帮我写一篇关于 Spring AI 工程落地的文章并发布到我的博客”。这里我用的是 REST 接口,后面也可以升级成 WebSocket 长连接。

Agent 层是核心,包含 ChatClient(Spring AI 提供的同步调用封装)、MCP 工具注册表、状态管理器。ChatClient 把用户指令发给大模型,模型在生成过程中根据工具描述决定调用哪个 MCP 工具。工具注册表是 Spring AI 在启动时自动从 MCP 客户端获取的,我不需要手动注册。

工具层是具体的 MCP Server,暴露三个核心工具:generate_article(生成文章草稿)、review_article(人工审核确认)、publish_article(执行发布)。这三个工具内部的实现分别对接大模型的生成能力、内部审核页面、各平台的发布 API。

这三层在一个 Spring Boot 进程里就能跑通,不需要拆微服务。但为了将来扩展,我在接口设计上做了隔离,MCP Server 端的工具实现都是独立的类,未来如果要拆出去单独部署,只需要把它们打包成独立进程,再改一下 Spring AI 的客户端配置指向远程地址即可。

3. 动手搭建:依赖配置与服务端开发

3.1 初始化项目与依赖引入

我用的是 Spring Initializr 生成的项目基础,然后手动往 pom.xml 里补充 Spring AI 相关依赖。Spring AI 的依赖管理走的是专门的 BOM,需要先在 dependencyManagement 里声明版本。我用的版本是 spring-ai-bom 1.0.0,这个版本在 2025 年已经正式 GA,稳定性和文档齐全度都没有问题。如果你在 2025 年年中之后看到更新的版本,可以直接用最新稳定版。

pom.xml 里四个核心依赖是这样的:

  • spring-ai-starter-model-openai:负责接入 OpenAI 兼容的 chat 模型。
  • spring-ai-starter-mcp-client:负责 MCP 客户端能力。
  • spring-ai-mcp-server-webmvc:负责 MCP 服务端暴露(用 WebMVC 方式)。
  • spring-boot-starter-data-jdbc 和 h2:负责发布任务的持久化。

其中 MCP 服务端的暴露方式有一个细节:Spring AI 支持两种传输方式,一种是基于 Socket 的标准传输,另一种是基于 HTTP 的 WebMVC 传输。我选的是 HTTP 方式,这样同一个 Spring Boot 应用既能对外暴露 REST API,又能作为 MCP Server 接收其他 MCP 客户端的请求。如果你只是在本机做工具,选 Socket 方式并配合本地进程调用也很方便。

3.2 MCP Server 服务端核心代码实现

自动发帖工具的 MCP Server 端,我定义了三个工具,先看最核心的 publish_article 工具。它的实现不复杂,但要注意几个设计点:工具名称要语义化,让模型一眼就能懂;description 要写得详细,包括参数含义、成功失败判定标准;内部实现要捕获所有异常并转成适合模型理解的错误信息。

我把核心代码简写成下面这个样子:

@Component @Tool public class ArticleTools { private final PlatformClient platformClient; public ArticleTools(PlatformClient platformClient) { this.platformClient = platformClient; } @Tool(description = "发布一篇技术文章到目标平台。platform 可选值:techblog、devto、csdn。返回发布结果和文章链接。") public PostResult publishArticle(String title, String content, String tags, String platform) { try { String url = platformClient.publish(platform, title, content, tags); return new PostResult(true, "发布成功", url); } catch (Exception e) { return new PostResult(false, "发布失败:" + e.getMessage(), null); } } }

这段代码里最关键的地方不是那几行调用,而是返回类型的定义。Spring AI 的 MCP 工具默认会把返回的 Java 对象序列化成 JSON 文本返回给模型。所以 PostResult 这个结构体要设计得对模型友好,最好不要直接抛异常,而是把错误信息放到返回结果里。模型看到 PostResult 里的 success=false 和错误消息,才会知道接下来该怎么处理。你直接把异常抛出去,模型看到的只是一段堆栈,它很难据此做出下一步决策。

3.3 工具的定义规范与参数说明技巧

MCP 工具的参数说明直接影响模型调用的准确性。在 Spring AI 的 @Tool 注解里,参数说明主要靠方法参数上的注释和 @Tool 注解的 description 字段。我总结出三个实践经验:

  • 参数名要完整,不要缩写。比如 platform 比 plt 好,publishType 比 pt 好。模型不是编译器,它靠语义理解参数,缩写会显著增加理解偏差。
  • description 里要写清楚可选值范围。platform 的可选值有哪些,直接在 description 里列出,模型就不会传一个不存在的平台名。
  • 必填参数和可选参数的排列顺序要固定。Spring AI 的代码生成机制里,必填参数会放在前面,可选参数后面加默认值,这个顺序问题能最小化模型的误传。

针对文本长度,我建议在工具内部做二次校验。模型生成的标题、正文可能超出平台限制,如果直接调平台 API 会报错。我在 publishArticle 内部先检查 content 的长度,如果超限就直接返回一个带提醒的失败结果,并建议模型拆分或精简。这种主动校验比让平台 API 返回错误再让模型自己猜要高效得多。

3.4 MCP 客户端的配置与工具发现机制

MCP Server 写完以后,接下来要让 Agent 应用能够发现这些工具。我在 application.yml 里做了如下配置:

spring: ai: model: openai: api-key: ${LLM_API_KEY} base-url: ${LLM_BASE_URL} chat: options: model: ${LLM_MODEL_NAME} mcp: client: enabled: true connections: local-publisher: urls: [ "http://localhost:8080/mcp" ]

这里需要特别注意的地方是 url 的配置。如果是同一个 Spring Boot 进程内同时启用客户端和服务端,可以使用 HTTP 方式指向本服务的 /mcp 端点。Spring Boot 启动时,MCP 客户端会自动拉起握手请求,获取服务端声明的工具列表,并把工具定义注入到 ChatClient 的上下文里。我刚才在配置里写的 local-publisher 是连接名称,可以随便起,但最好起得有意义,日志排查时一眼就能看出来是哪条连接。

配置完成后,我写一个简单的 ChatClient 调用测试,确认工具发现是否成功。用下面这段代码:

@Component public class PublishingAgent { private final ChatClient chatClient; public PublishingAgent(ChatClient.Builder builder) { this.chatClient = builder.build(); } public String execute(String userInstruction) { return chatClient.prompt(userInstruction).call().content(); } }

这段代码看起来跟普通的 ChatClient 调用一模一样,没有任何 MCP 相关的代码。但正因为 Spring AI 在启动时已经发现并注册了 MCP 工具,模型在收到“发布一篇文章”的指令时,会自动选择调用 publishArticle 工具,然后生成最终回复。

4. 自动发帖核心流程:从指令到发布的全链路实现

4.1 指令解析与 Agent 编排

自动发帖工具要做到“一句话发帖”的效果,背后的指令解析逻辑必须做好。用户可能输入的是各种口语化的内容,比如“帮我把这篇 Spring AI 的文章发到 CSDN”,或者“写一篇 MCP 入门文章,发到掘金吧”。这些输入里,平台、主题、操作意图都是非结构化的,需要模型自己抽出来。

我在 Agent 编排里没有写任何硬编码的 intent 识别规则,而是依赖模型的理解能力。这是 MCP 架构的优势所在。如果我用传统规则,需要写一堆关键词匹配来识别平台名、主题词,而且用户换个说法就失效。现在模型根据工具描述和对话上下文,自己就能完成意图识别。我的工作是把整个流程拆成若干步,通过记忆机制让模型按顺序执行。

实际编排逻辑是:第一步,定义一个系统提示词,告诉模型“你是内容发布助手,你需要根据用户的指令生成文章内容,并在用户确认后发布到指定平台”。第二步,让 ChatClient 支持多轮对话,保存上一轮的生成结果。第三步,在发布工具中设计 preview_only 参数,让首轮调用只返回文章预览,提醒用户确认。第四步,用户确认后,模型再以确认指令触发正式发布。

4.2 内容生成与多平台适配

内容生成是自动发帖工具体验的敏感部分。我一开始直接把用户指令丢给模型,让它边生成边发帖,结果发现生成质量不稳定。后来我改为两步走:先让模型生成一篇完整的文章草稿,存到数据库,再把草稿作为参数传给发布工具。这样做的好处是,生成过程和发布过程可以分开回滚,而且草稿可以供人工修改后再发布。

多平台适配是另一个容易踩坑的地方。不同技术平台的 Markdown 支持和标签体系都不一样,有的平台支持自定义标签,有的平台标签有数量限制。我在 publishArticle 工具里用一个 Map 维护每个平台的特殊配置,比如每个平台的标签上限、是否支持自定义域名、正文最大长度。调用平台 API 前,工具内部会先做一次归一化处理,把模型传的统一格式转换成平台需要的格式。

这里我想到一个细节:模型生成的 tags 数量可能超出平台限制。如果是传统代码,我会在服务端把多余的标签截断;但 MCP 的好处是,我可以把截断信息写进返回结果,让模型知道“标签被截断了”,这样模型在后续对话里会主动提示用户。这种可感知的副作用处理,是普通 REST API 很难做到的,因为返回值只是一段固定结构,模型并不知道背后发生了什么。

4.3 发布任务状态机与重试机制

发帖这个行为天然具有不确定性:网络超时、平台限流、参数校验失败,都可能造成发布失败。我设计了一个简单的状态机:草稿(DRAFT)、待审核(PENDING)、已审核(APPROVED)、发布中(PUBLISHING)、发布成功(SUCCESS)、发布失败(FAILED)。数据库表里有对应的状态列和重试次数列。

状态机的流转完全由 Agent 驱动。MCP 工具返回发布结果后,状态更新代码会把最新的状态写回数据库。如果发布失败且重试次数小于 3,工具会返回一个带有重试建议的失败信息,模型可以选择稍后重试或调整参数。这个设计让我在测试阶段省了很多心力,即使模型某次发布真的失败了,人工也能从后台页面看到失败原因,手动修复。

@Service public class PublishTaskService { @Scheduled(fixedDelay = 30000) public void retryPendingTasks() { List<PublishTask> tasks = taskRepository.findByStatus(PublishStatus.FAILED); for (PublishTask task : tasks) { if (task.getRetryCount() < 3) { platformClient.publish(task.executablePayload()); taskRepository.markAsSuccess(task.getId()); } else { taskRepository.markAsFinalFail(task.getId()); } } } }

这个定时任务是一个兜底方案。MCP 工具调用失败后,模型不一定真的会执行重试,但定时任务会尽职尽责地清扫失败队列。我觉得这是做自动化工具必须具备的工程思维:不管 AI 合不合作,底层的可靠性保障机制必须独立存在。

5. 实测效果与调优笔记

5.1 我实测的一组运行数据

工具跑通以后,我做了几轮测试。第一批是基础功能测试,输入“写一篇关于 MCP 工具开发的入门文章,发到测试平台”,模型先调用了 generate_article 工具生成约 1200 字的草稿,然后调用 publish_article 工具以 preview 模式返回预览内容,我再手动确认后发布。整个流程用了约 40 秒,其中生成内容占 30 秒左右,发布调用占 5 秒左右。

第二批是多平台测试,我故意让模型一次性发布到两个平台。模型并行调用了两次 publish_article 工具,第一次发布成功,第二次因为目标平台标签策略不支持多标签而失败。失败信息通过 PostResult 返回后,模型自动调整了 tags 参数并重新调用,最终成功。这个过程说明 MCP 工具调用不是“响一声就完事”,而是整个 Agent 思考链路的一部分。

第三批是错误处理测试,我在平台 API 里故意注入了一个鉴权错误。模型调用工具得到错误后,并没有直接把错误抛给用户,而是自己拼了一段说明:“发布失败了,原因是令牌无效,请检查平台凭证。”这个表现我觉得是合格的,因为模型理解了工具返回的结构化错误信息,而不是简单堆砌堆栈。

这三批测试下来,最耗时的不是发布本身,而是内容生成。如果你想把发帖工具做得更高效,建议对文本生成长度做限制,并在系统提示词里要求模型控制在 800 到 1500 字之间,这样既能保证质量,又能明显提升响应速度。

5.2 工具性能优化与 Token 管控

MCP 工具的注册会给每次模型调用增加额外的 Token 消耗,因为工具描述本身会被发送给模型。我遇到过工具描述太长导致 Prompt Token 暴涨的情况,尤其是我第一次写 publishArticle 工具时,description 写了将近 200 个字,每次请求都带着这 200 个字跑,成本很可观。

解决办法是精简工具描述,把必选信息留着,冗余解释删掉。我最后把 publishArticle 的 description 压到了 80 个字以内,只覆盖动作、目标平台和关键参数。这个数字是在实测中平衡出来的:太短模型理解不充分,太长浪费 Token。另外,Spring AI 支持给 ChatClient 设置系统提示词,建议在系统提示词里提醒模型“优先复用工具描述中的参数值,不要额外扩展”,能减少一些无效 Token。

5.3 发布结果回传的细节处理

发布成功后,平台返回的文章链接对用户来说是最重要的信息。我在这里踩过一个坑:MCP 工具返回的 PostResult 只包含 URL 字段,但模型在最终回复时经常只是简单说“发布成功”,没有把 URL 带出来。原因是模型默认认为用户只关心结果,不关心链接。后来我在 PostResult 里把链接字段改名为 articleUrl,并且在 description 里写了一句“请向用户展示 articleUrl 字段的完整链接”,这个问题就消失了。

这个优化让我更加确定,MCP 工具与模型的交互才是决定 Agent 体验的关键,底层的功能实现反而是次要的。我建议所有做 MCP 工具的人,在写完代码后一定要花时间打磨工具描述,这对模型的表现影响巨大。

6. 常见问题排查与避坑指南

6.1 工具列表为空,模型不调用任何工具

这个问题我遇到太多次了。第一反应检查 application.yml 里 mcp 客户端连接配置是否有误,url 地址是不是写对了。第二反应是看 Spring 启动日志,Spring AI 会输出类似“Registered tools: [publishArticle, generateArticle]”的日志,如果日志里没有工具列表,说明服务端工具没有被成功发现。第三反应是确认 ChatClient 是不是真正注入了工具,因为这个过程是动态的,如果你用了缓存或者代理模式,工具发现可能会失败。

还有一种很隐蔽的情况:你给 ChatClient 设置了自定义的 ToolCallbacks 列表,覆盖了默认的 MCP 工具集合。Spring AI 里有个方法叫 ToolCallbacks.withToolCallbacks,如果你手动传入了自定义列表,MCP 自动发现的工具就不会生效。我的建议是尽量不要混用,除非你非常清楚自己在干什么。

6.2 模型反复调用同一个工具导致死循环

自动发帖工具踩过一个大坑:模型发布失败后,不修改参数,原地重试同一个工具调用了五次,白白消耗 Token。后来我在工具返回结果里加入了 differentiate 字段,用来区分不同的失败类型。比如参数错误和网络超时是两种性质完全不同的失败,模型看到参数错误就不会重试,而看到网络超时就会自动等待后重试。

另外,Spring AI 本身没有限制单次对话中的工具调用次数,这个需要你自己在 Agent 层控制。我加了一个简单的计数器,如果单次对话中模型对同一个工具连续调用超过三次,就直接中止并把控制权交给用户。这个保护机制很重要,生产环境里一定要有。

6.3 多平台发布时的并发冲突问题

Spring AI 的 ChatClient 在默认情况下会按照模型返回的顺序串行执行工具调用,所以并发冲突并不常见。但我还是遇到过一种情况:模型生成了两篇文章发布到同一个平台,第二次调用时平台 API 要求参数唯一,第二次发布因为重复内容被拒。这个问题的根因是模型可能把同一段内容排了两遍,我通过在发布工具里维护一个最近发布内容的缓存,用标题相似度做去重,简单有效。

如果未来你处理更大的并发量,建议给每个 MCP Server 的调用链加上独立的事务边界和超时控制。我在所有调用外部 API 的代码上都加了 5 秒超时,防止平台接口卡死导致虚拟线程池被占满。这个在 Java 21 虚拟线程的场景下尤其重要,因为虚拟线程虽然便宜,但阻塞等待外部 IO 的时间是无法完全忽略的。

6.4 常见错误速查表

错误现象可能原因排查方法
启动时 MCP 连接失败url 配置错误或服务未启动检查日志中 MCP 握手请求状态码
模型不识别任何工具工具发现未完成或 ToolCallbacks 被覆盖查看启动日志的 Registered tools 列表
工具调用报参数缺失模型没有正确映射方法参数名检查 @Tool 注解的参数说明是否清晰
发布成功但提示失败平台 API 返回码与工具判定逻辑不一致查看平台 API 想返回码映射,调整工具内部判定
数据库出现脏数据工具调用过程中发生异常导致事务回滚给工具方法加上 @Transactional 并定义回滚边界

这里多说一句关于事务的问题。MCP 工具执行和方法调用不同,它可能从模型侧发起并横跨多个请求。我建议把事务控制保持在单个工具方法内部,不要让一个工具调用去操纵整个业务流程的事务。否则出现问题时会很难定位,因为模型和数据库都在互相推锅。

7. 经验总结:把 MCP 工具做“薄”,把 Agent 做“厚”

自动发帖工具做完以后,我最大的体会是:MCP 工具应该做“薄”,Agent 编排应该做“厚”。工具端只负责可靠执行,所有智能决策和行为编排都放在 Agent 层,用模型能力去驱动。

所谓使工具做“薄”,就是工具内部逻辑尽量简单,只做单一能力,比如生成文章就是生成文章,发布文章就是发布文章,不要强行把一个工具做成“生成并发布”。这样模型在调用时会非常灵活,可以选择单独生成,也可以选择生成后再发布,还可以组合其他工具做更复杂的事情。我第一版工具试图把“生成并发布”合并成一个工具,结果模型在用户只想生成不想发布时非常为难,后来拆开以后这个问题自然消失了。

Agent 做“厚”的意思是,让模型尽量多地参与决策和调度。Spring AI 的体系里,你可以写系统提示词、设置工具选择策略、甚至给模型返回结果做后处理。这些机制可以让 Agent 表现得像个有工作经验的人,而不仅仅是几个工具的函数调用。自动发帖工具后期迭代时,我甚至不用改工具代码,只需要修改系统提示词,就能让模型按不同的风格生成文章,或者按不同的流程安排审核节点。

这个工具后续我打算扩展的方向有两个:一是接入更多的 MCP 工具,比如获取实时热点、抓取资料文献,让内容生成的信息源更丰富;二是把发布结果通过 MCP 的反向通道推送给用户,做成一个更完整的双向交互。如果你也在做类似的自动化工具,我的建议很直接:先别追求复杂的架构,用 Spring AI 的 MCP Starter 把最小可用版本跑起来,再根据真实踩坑逐步打磨工具描述和编排逻辑。这套路线花不了多少时间,却能让你对整个链路理解得非常透彻。

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

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

立即咨询