ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

将 DeepSeek 集成到 Spring Boot 项目实现通过 AI 对话方式操作后台数据:TaoToken 统一 Key 配置与 MCP 工具调用骨架

将 DeepSeek 集成到 Spring Boot 项目实现通过 AI 对话方式操作后台数据:TaoToken 统一 Key 配置与 MCP 工具调用骨架 1. 后台管理为什么需要一句对话查数据后台管理系统做久了你会发现一个很尴尬的现象运营同学想查「年龄 28 岁的用户有哪些」第一反应不是打开筛选表单而是直接在企业微信里问开发。开发打开数据库客户端写一条 SQL截图发过去。一天下来这种碎片化查询能吃掉两三个小时。问题的本质不是表单做得不好而是自然语言到数据操作之间缺了一层翻译。传统做法是给每个查询场景写一个接口运营点按钮触发。但运营的需求是发散的你不可能预判所有组合条件。这时候大模型的价值就出来了它能理解「帮我看看上周注册、还没下单的用户」然后决定调用哪个工具、传什么参数。我试过把这套链路拆成三块Spring Boot 后台负责暴露数据操作能力MCP 协议负责把能力标准化成工具DeepSeek 负责理解意图并选择工具。三者串起来就实现了「一句对话完成一次后台数据查询」。这篇文章面向的是有 Spring Boot 基础、想给后台加 AI 对话能力的后端同学。你不需要提前懂 MCP我会从配置讲到验证最后你能跑通一句「查询年龄 28 的用户」并拿到真实数据。核心检索词就三个DeepSeek 负责意图理解Spring Boot 负责业务承载MCP 负责工具调用标准化。整个链路里最容易卡住的地方不是代码而是 Key 管理和通道配置。多个模型、多个环境、多个同事共用一套后台Key 散落在各个 yml 里改一次要重新打包。所以我会用 TaoToken 做统一 Key 和 API 通道把模型接入这件事从业务代码里彻底剥离出来。2. TaoToken 统一 Key 与 API 通道的前置准备在动手写 MCP 工具之前先把模型接入这层理顺。很多同学一上来就在application.yml里硬编码api-key: sk-xxx本地跑通了一提交代码就出事。更麻烦的是后台可能同时要用 DeepSeek 做对话、用别的模型做摘要每个模型一套 Key管理成本直线上升。TaoToken 在这里扮演的角色是统一入口你只需要在它那边生成一个 Key配置一个 base-urlSpring Boot 侧就把它当成标准的 OpenAI 兼容接口来用。DeepSeek 的对话能力通过这个通道进来业务代码完全不用关心底层是哪家模型。具体操作路径是这样的先到官网注册并进入控制台在 API Keys 页面生成一个 Key。这个 Key 就是你后面application.yml里要填的值。生成之后建议单独建一个环境变量或者配置中心条目不要直接写死在代码里。注意Key 一旦生成就只显示一次复制后妥善保存。如果怀疑泄露直接在控制台删除重建比到处改配置快得多。拿到 Key 之后你需要确认两件事一是 base-url 指向 TaoToken 的 API 地址https://taotoken.net/api二是模型名填deepseek-chat。这两项配好Spring AI 的 OpenAI starter 就能正常发起请求。如果你还想用 Claude 系列做代码相关的工具调用可以在控制台看看 Coding Plan 的额度长期跑 Agent 场景会更划算。这一步做完你手里应该有一个可用的 Key 和一个明确的 base-url。接下来才是 Spring Boot 项目里的实际配置。3. Spring Boot 项目可复制的 application.yml 配置先确认环境JDK 17 以上Spring Boot 3.x。这两个是硬门槛JDK 低于 17 编译 Spring AI 的依赖会直接报错。我踩过的坑就是本地默认 JDK 8折腾半天以为是依赖冲突其实是版本不够。依赖部分用 Spring AI 的 BOM 统一管理版本核心引入三个 starterMCP 服务端、MCP 客户端、OpenAI 模型适配。MCP 服务端负责把后台的数据操作暴露成工具MCP 客户端负责连接服务端并加载工具列表OpenAI starter 负责和 DeepSeek 对话。dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.1/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-server-webflux/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency /dependencies然后是application.yml。这里的关键是把模型通道指向 TaoToken同时配置 MCP 客户端的连接方式。我用的是 SSE 连接本地调试最直观服务端启动后客户端能自动发现工具。spring: ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api chat: options: model: deepseek-chat temperature: 0.2 mcp: client: type: sync name: spring-mcp-client sse: connections: server1: url: http://localhost:9800temperature设成 0.2 是有意为之。后台数据查询需要的是稳定和准确不是创意。温度太高模型可能把「年龄 28」理解成「年龄 28 左右」生成的查询条件就飘了。api-key用${TAOTOKEN_API_KEY}占位实际值通过环境变量注入。本地开发可以在 IDE 的运行配置里加环境变量生产环境走配置中心。这样代码仓库里永远不会出现明文 Key。MCP 服务端的端口我习惯用 9800客户端用 9801。两个服务可以放在同一个 Spring Boot 应用里也可以拆成两个进程。拆开的好处是服务端可以独立部署在能访问数据库的内网机器上客户端只负责对话逻辑安全边界更清晰。4. MCP 工具注册骨架与对话到数据操作的映射配置只是通道真正让「一句话查数据」跑起来的是工具注册。MCP 的核心思想是把后台的每个数据操作能力包装成一个带描述的工具模型根据用户的话去匹配工具、填参数。先看服务端的工具注册配置。这段代码的作用是扫描所有实现了McpTool接口的 Bean把它们统一注册成 MCP 工具。你新增一个查询能力只要实现接口加注解不用改注册逻辑。Configuration AllArgsConstructor public class ToolCallbackProviderConfig { private final ApplicationContext applicationContext; Bean public ToolCallbackProvider methodToolCallbackProvider() { MapString, McpTool mcpBeanMap applicationContext.getBeansOfType(McpTool.class); return MethodToolCallbackProvider.builder() .toolObjects(mcpBeanMap.values().toArray()) .build(); } }接下来是具体的工具实现。这里以「根据年龄查询用户」为例Tool的 description 是给模型看的写得越清楚模型选工具的准确率越高。ToolParam描述参数含义模型据此从用户的话里抽取年龄值。Slf4j Service AllArgsConstructor public class McpUserService implements McpTool { private final IUserService userService; Tool(description 根据年龄查询用户列表返回姓名、年龄、注册时间) Transactional(rollbackFor {RuntimeException.class, Exception.class}) public String queryUserByAge( ToolParam(description 用户年龄整数) Integer age) { PageUser page new Page(1, 10); ListUser userList userService.list( page, Wrappers.UserlambdaQuery() .eq(User::getAge, age) .orderByAsc(User::getName)); return JSON.toJSONString(userList); } }这里有个设计细节值得说工具返回的是 JSON 字符串不是对象。因为 MCP 传输层需要序列化直接返回对象容易在客户端反序列化时出问题。用 JSON 字符串最稳模型拿到后也能自己解析成可读格式。客户端侧的核心是把 MCP 工具加载进来绑定到 ChatClient 上。这样每次对话模型都能看到当前可用的工具列表自主决定是否调用。Slf4j Service public class ChatService { private final ChatClient chatClient; public ChatService(OpenAiChatModel openAiChatModel, ListMcpSyncClient mcpSyncClientList) { var provider new SyncMcpToolCallbackProvider(mcpSyncClientList); this.chatClient ChatClient.builder(openAiChatModel) .defaultTools(provider) .build(); } public String ask(String prompt) { return chatClient.prompt(prompt).call().content(); } }对话到数据操作的映射关系本质上是三层用户说「查一下 28 岁的用户」DeepSeek 识别出意图是查询、实体是用户、条件是年龄等于 28然后匹配到queryUserByAge这个工具填入参数 28工具执行后返回数据DeepSeek 再把 JSON 转成自然语言回复。如果你想让模型支持更复杂的查询比如「查询年龄 28 且名字带张的用户」有两种做法一是给工具加更多参数二是注册多个细粒度工具让模型组合。我建议后者工具职责单一模型组合起来更灵活也更容易排查问题。5. 本地启动与验证跑通一句对话查询配置和代码都就位后启动顺序很重要。先起 MCP 服务端再起客户端。服务端启动后监听 9800 端口客户端启动时会去连这个端口拉取工具列表。启动服务端后你可以先验证工具是否注册成功。访问 SSE 端点http://localhost:9800/sse如果连接保持不断开说明服务端正常。更直接的验证是看客户端启动日志里面会打印加载到的工具列表。# 启动服务端 java -jar mcp-server.jar # 观察日志确认工具注册 # 应看到类似Registered tools: [queryUserByAge]客户端启动后日志里会打印toolCallbacks的内容你能看到每个工具的 name、description、参数 schema。这一步如果工具列表为空说明客户端没连上服务端检查application.yml里的 url 是否写对。验证对话链路我习惯先用一个最简单的 HTTP 接口触发。客户端暴露一个/chat接口传 prompt 进去看返回。curl http://localhost:9801/chat?prompt查询年龄28的用户预期结果是模型先返回一段思考过程如果开了 reasoning然后调用queryUserByAge工具拿到数据后组织成自然语言。你会在日志里看到工具调用的入参和返回值这是排查问题的关键线索。如果一切正常返回内容大概是「找到 3 位年龄 28 岁的用户张三、李四、王五注册时间分别是……」。到这里一句对话完成一次后台数据查询的链路就通了。再进一步你可以把这个/chat接口接到前端聊天窗口或者接到企业微信机器人。后台运营直接在聊天框里输入自然语言就能拿到数据不用再找开发。6. 本篇常见错误排查清单启动报错Unsupported class file major versionJDK 版本不够。Spring AI 1.0.1 要求 JDK 17 以上检查java -version如果是 8 或 11换 JDK 17 重新编译。客户端日志里工具列表为空MCP 服务端没启动或者客户端配置的 url 不对。先确认服务端 9800 端口在监听再检查application.yml里sse.connections.server1.url是否指向正确地址。如果服务端和客户端不在同一台机器localhost 要换成实际 IP。模型不调用工具直接编造答案工具的 description 写得太模糊。模型选工具靠的是 description 和用户意图的语义匹配如果 description 只写「查询用户」模型不知道能查什么条件。改成「根据年龄查询用户列表返回姓名、年龄、注册时间」匹配准确率会明显提升。调用工具时报参数类型错误ToolParam的类型和模型抽取的值不匹配。比如年龄定义成 Integer但模型传了「二十八」这种中文。解决办法是在 description 里明确「整数」或者在工具内部做一次转换兜底。返回 401 或鉴权失败检查 TaoToken 的 Key 是否正确注入。用${TAOTOKEN_API_KEY}占位时确认环境变量真的传进去了。可以在启动日志里打印一下配置的 base-url确认指向https://taotoken.net/api而不是别的地址。SSE 连接频繁断开本地开发常见通常是客户端和服务端版本不匹配。确保两边用的 Spring AI 版本一致都是 1.0.1。如果还是断换成 WebSocket 连接方式稳定性更好。排查这类问题的通用思路是先看日志里工具有没有注册成功再看模型有没有发起工具调用最后看工具执行有没有报错。三段定位基本能覆盖九成问题。7. 把对话能力接进真实后台的下一步链路跑通只是起点。真实后台场景里你还需要考虑几件事权限控制不能让所有登录用户都能查全量数据查询范围限制模型生成的查询条件要加租户隔离审计日志每次对话触发的数据操作都要留痕。这些都可以在工具实现层做。比如在queryUserByAge里从当前登录上下文取租户 ID拼到查询条件里。模型不需要知道租户的存在它只负责理解用户意图安全边界由你的业务代码守住。如果你打算把这套能力长期跑在编码或 Agent 场景里可以看看 TaoToken 的 Coding Plan额度更充足适合高频调用。接入文档里有更详细的参数说明遇到配置问题可以直接对照排查。模型对话页面也能快速验证 Key 是否可用不用每次都启动整个 Spring Boot 项目。最后留一个实用技巧给工具加一个「查询字段白名单」模型只能查你允许的字段。这样即使模型理解偏了也不会把敏感字段带出来。后台数据操作这件事让 AI 做翻译让代码做守门人分工清楚才跑得稳。
返回列表