当前位置: 首页 > news >正文

观察Springboot AI-Function Tools 执行过程

观察Springboot AI-Function Tools 执行过程

  • Springboot AI-Function Tools 执行流程
    • 构造一个Spring AI MCP Server
    • 观察Springboot AI应用程序将MCP(Stdio)结合Deepseek的过程
      • 单元测试
      • 第一次对模型提问
      • 第一次模型返回
      • 第二次对模型提问
      • 第二次模型返回
    • 观察Springboot AI应用程序将MCP(sse)结合Deepseek的过程
      • 修改并启动MCP服务
      • 单元测试
      • 第一次对模型提问
      • 第一次模型返回
      • 第二次对模型提问
      • 第二次模型响应
    • 调用时序图
    • 附录
      • POM依赖

Springboot AI-Function Tools 执行流程

在这里插入图片描述

构造一个Spring AI MCP Server

一个简易的查询公司员工的MCP Server工程Demo

下载MCP工程后达成JAR,并复制jar的完整路径
在这里插入图片描述

观察Springboot AI应用程序将MCP(Stdio)结合Deepseek的过程

单元测试

@Test
public void mcp_model_stdio_call_tool_test(){ServerParameters serverParameters = ServerParameters.builder("java").args("-Dspring.ai.mcp.server.stdio=true", "-jar", "/Users/jiangyangang/gitee_project/ai-mcp-server-demo/target/ai-mcp-server-demo-1.0.0.jar").env(null).build();StdioClientTransport stdioClientTransport = new StdioClientTransport(serverParameters);McpSyncClient mcpSyncClient = McpClient.sync(stdioClientTransport).requestTimeout(Duration.ofSeconds(50L)).build();mcpSyncClient.initialize();OpenAiApi openAiApi = OpenAiApi.builder().baseUrl("https://api.deepseek.com/").completionsPath("v1/chat/completions").embeddingsPath("v1/embeddings").restClientBuilder(RestClient.builder().requestInterceptor(new RestClientFilter())).apiKey("sk-529fb4e1f9e448b6bbe2a252a0f5c796").build();OpenAiChatModel chatModel = OpenAiChatModel.builder().openAiApi(openAiApi).defaultOptions(OpenAiChatOptions.builder().model("deepseek-chat").build()).build();ChatClient chatClient = ChatClient.builder(chatModel).defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpSyncClient).getToolCallbacks()).defaultAdvisors(new SimpleLoggerAdvisor()).build();String system = "你是一个查询员工信息的小助手,你可以通过我给你的MCP接口【getSalaryDetailInfo】来查询员工的信息,包括地址和职位";String content = chatClient.prompt(new SystemPromptTemplate(system).create()).user("员工蒋颜刚的职位是什么?").call().content();System.out.println(content);
}

第一次对模型提问

提问内容(缩略)

在提问时,spring ai会在promote中加入**tools**字段来告诉模型现在有哪些MCP服务可以调用,

parameters :mcp服务中需要提供哪些参数

name :mcp服务接口名称

description :mcp服务描述

required :哪些字段是必须要传的

{"messages": [{"content": "你是一个查询员工信息的小助手,你可以通过我给你的MCP接口【getSalaryDetailInfo】来查询员工的信息,包括地址和职位","role": "system"},{"content": "员工蒋颜刚的职位是什么?","role": "user"}],"model": "deepseek-chat","stream": false,"tools": [{"type": "function","function": {"description": "获取公司某个员工详细信息的接口","name": "JavaSDKMCPClient_getSalaryDetailInfo","parameters": {"additionalProperties": false,"type": "object","properties": {"request": {"type": "object","properties": {"name": {"type": "string","description": "员工名字"}},"required": ["name"]}},"required": ["request"]}}}]
}

第一次模型返回

tool_calls :AI应用重新应该调用哪些mcp服务

name :mcp服务接口名字

arguments :mcp服务接口字段和对应的参数

tool_call_id : 调用MCP服务的请求流水ID,由大模型生成

{"id": "6e069021-23b8-41d5-a8f7-44d06b20161a","object": "chat.completion","created": 1757908504,"model": "deepseek-chat","choices": [{"index": 0,"message": {"role": "assistant","content": "我来帮您查询员工蒋颜刚的职位信息。","tool_calls": [{"index": 0,"id": "call_00_sVqnvhaz8xn3O8aAq8MM6FsB","type": "function","function": {"name": "JavaSDKMCPClient_getSalaryDetailInfo","arguments": "{\"request\": {\"name\": \"蒋颜刚\"}}"}}]},"logprobs": null,"finish_reason": "tool_calls"}],"usage": {"prompt_tokens": 225,"completion_tokens": 38,"total_tokens": 263,"prompt_tokens_details": {"cached_tokens": 0},"prompt_cache_hit_tokens": 0,"prompt_cache_miss_tokens": 225},"system_fingerprint": "fp_08f168e49b_prod0820_fp8_kvcache"
}

第二次对模型提问

在第二次提问时,AI应用程序会新增一个角色tool 用来告诉模型上一笔调用mcp的结果,大模型根据tool_call_id 就能够进行工具的关联。

{"messages": [{"content": "你是一个查询员工信息的小助手,你可以通过我给你的MCP接口【getSalaryDetailInfo】来查询员工的信息,包括地址和职位","role": "system"},{"content": "员工蒋颜刚的职位是什么?","role": "user"},{"content": "我来帮您查询员工蒋颜刚的职位信息。","role": "assistant","tool_calls": [{"id": "call_00_sVqnvhaz8xn3O8aAq8MM6FsB","type": "function","function": {"name": "JavaSDKMCPClient_getSalaryDetailInfo","arguments": "{\"request\": {\"name\": \"蒋颜刚\"}}"}}]},{"content": "[{\"text\":\"{\\\"address\\\":\\\"深圳\\\",\\\"job\\\":\\\"后端开发工程师\\\"}\"}]","role": "tool","name": "JavaSDKMCPClient_getSalaryDetailInfo","tool_call_id": "call_00_sVqnvhaz8xn3O8aAq8MM6FsB"}],"model": "deepseek-chat","stream": false,"tools": [....]
}

第二次模型返回

{"id": "9e376a0e-8cb7-41f2-be98-b973996d418f","object": "chat.completion","created": 1757908508,"model": "deepseek-chat","choices": [{"index": 0,"message": {"role": "assistant","content": "根据查询结果,员工蒋颜刚的职位是**后端开发工程师**,工作地点在深圳。"},"logprobs": null,"finish_reason": "stop"}],"usage": {"prompt_tokens": 287,"completion_tokens": 22,"total_tokens": 309,"prompt_tokens_details": {"cached_tokens": 256},"prompt_cache_hit_tokens": 256,"prompt_cache_miss_tokens": 31},"system_fingerprint": "fp_08f168e49b_prod0820_fp8_kvcache"
}

观察Springboot AI应用程序将MCP(sse)结合Deepseek的过程

修改并启动MCP服务

在ai-mcp-server-employee服务中加入webflux依赖

<!-- sse 模式需要配置 webflux -->
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-server-webflux-spring-boot-starter</artifactId>
</dependency>

之后启动MCP服务

单元测试

@Test
public void mcp_model_sse_call_tool_test(){HttpClientSseClientTransport sseClientTransport = HttpClientSseClientTransport.builder("http://127.0.0.1:9102/").sseEndpoint("sse").build();McpSyncClient mcpSyncClient = McpClient.sync(sseClientTransport).requestTimeout(Duration.ofSeconds(5L)).build();mcpSyncClient.initialize();
//McpSchema.ListToolsResult listToolsResult = mcpSyncClient.listTools(null);System.out.println(JSONObject.toJSONString(listToolsResult));OpenAiApi openAiApi = OpenAiApi.builder().baseUrl("https://api.deepseek.com/").completionsPath("v1/chat/completions").embeddingsPath("v1/embeddings").restClientBuilder(RestClient.builder().requestInterceptor(new RestClientFilter())).apiKey("sk-xxxx").build();OpenAiChatModel chatModel = OpenAiChatModel.builder().openAiApi(openAiApi).defaultOptions(OpenAiChatOptions.builder().model("deepseek-chat").build()).build();ChatClient chatClient = ChatClient.builder(chatModel).defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpSyncClient).getToolCallbacks()).defaultAdvisors(new SimpleLoggerAdvisor()).build();String system = "你是一个查询员工信息的小助手,你可以通过我给你的MCP接口【getSalaryDetailInfo】来查询员工的信息,包括地址和职位";String content = chatClient.prompt(new SystemPromptTemplate(system).create()).user("员工蒋颜刚的职位是什么?").call().content();System.out.println(content);
}

第一次对模型提问

提问内容(缩略)

在提问时,spring ai会在promote中加入**tools**字段来告诉模型现在有哪些MCP服务可以调用,

parameters :mcp服务中需要提供哪些参数

name :mcp服务接口名称

description :mcp服务描述

required :哪些字段是必须要传的

{"messages": [{"content": "你是一个查询员工信息的小助手,你可以通过我给你的MCP接口【getSalaryDetailInfo】来查询员工的信息,包括地址和职位","role": "system"},{"content": "员工蒋颜刚的职位是什么?","role": "user"}],"model": "deepseek-chat","stream": false,**"tools": [{"type": "function","function": {"description": "获取公司某个员工详细信息的接口","name": "JavaSDKMCPClient_getSalaryDetailInfo","parameters": {"additionalProperties": false,"type": "object","properties": {"request": {"type": "object","properties": {"name": {"type": "string","description": "员工名字"}},"required": ["name"]}},"required": ["request"]}}}]**
}

第一次模型返回

tool_calls :AI应用重新应该调用哪些mcp服务

name :mcp服务接口名字

arguments :mcp服务接口字段和对应的参数

tool_call_id : 调用MCP服务的请求流水ID,由大模型生成

{"id": "475fe411-4ac4-44d3-bc09-71a3ac895a73","object": "chat.completion","created": 1757683715,"model": "deepseek-chat","choices": [{"index": 0,"message": {"role": "assistant","content": "我来帮您查询蒋颜刚的职位信息。",**"tool_calls": [{"index": 0,"id": "call_00_5JnhTbbmeICprFEMbzgnNkxJ","type": "function","function": {"name": "JavaSDKMCPClient_getSalaryDetailInfo","arguments": "{\"request\": {\"name\": \"蒋颜刚\"}}"}}]**},"logprobs": null,"finish_reason": "tool_calls"}],"usage": {"prompt_tokens": 225,"completion_tokens": 37,"total_tokens": 262,"prompt_tokens_details": {"cached_tokens": 192},"prompt_cache_hit_tokens": 192,"prompt_cache_miss_tokens": 33},"system_fingerprint": "fp_08f168e49b_prod0820_fp8_kvcache"
}

第二次对模型提问

在第二次提问时,AI应用程序会新增一个角色**tool** 用来告诉模型上一笔调用mcp的结果,大模型根据**tool_call_id** 就能够进行工具的关联。

{"messages": [{"content": "你是一个查询员工信息的小助手,你可以通过我给你的MCP接口【getSalaryDetailInfo】来查询员工的信息,包括地址和职位","role": "system"},{"content": "员工蒋颜刚的职位是什么?","role": "user"},{"content": "我来帮您查询蒋颜刚的职位信息。","role": "assistant","tool_calls": [{"id": "call_00_frX3BDPuCRge9oBnwKWgBiCY","type": "function","function": {"name": "JavaSDKMCPClient_getSalaryDetailInfo","arguments": "{\"request\": {\"name\": \"蒋颜刚\"}}"}}]},{"content": "[{\"text\":\"{\\\"address\\\":\\\"深圳\\\",\\\"job\\\":\\\"后端开发工程师\\\"}\"}]","role": "tool","name": "JavaSDKMCPClient_getSalaryDetailInfo","tool_call_id": "call_00_frX3BDPuCRge9oBnwKWgBiCY"}],"model": "deepseek-chat","stream": false,"tools": [{"type": "function","function": {"description": "获取公司某个员工详细信息的接口","name": "JavaSDKMCPClient_getSalaryDetailInfo","parameters": {"additionalProperties": false,"type": "object","properties": {"request": {"type": "object","properties": {"name": {"type": "string","description": "员工名字"}},"required": ["name"]}},"required": ["request"]}}}]
}

第二次模型响应

{"id": "f7d8c859-1858-4421-9ea3-361e754ed4ee","object": "chat.completion","created": 1757684702,"model": "deepseek-chat","choices": [{"index": 0,"message": {"role": "assistant","content": "根据查询结果,员工蒋颜刚的职位是**后端开发工程师**,工作地点在深圳。"},"logprobs": null,"finish_reason": "stop"}],"usage": {"prompt_tokens": 286,"completion_tokens": 22,"total_tokens": 308,"prompt_tokens_details": {"cached_tokens": 256},"prompt_cache_hit_tokens": 256,"prompt_cache_miss_tokens": 30},"system_fingerprint": "fp_08f168e49b_prod0820_fp8_kvcache"
}

调用时序图

在这里插入图片描述

附录

POM依赖

<!-- spring ai 1.0.0 https://central.sonatype.com/artifact/org.springframework.ai/spring-ai-bom -->
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>1.0.0</version><type>pom</type><scope>import</scope>
</dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-openai</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-vector-store-pgvector</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-client-webflux</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-tika-document-reader</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.28</version>
</dependency>
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version>
</dependency>
http://www.dtcms.com/a/605320.html

相关文章:

  • 信贷风控建设的多维意义解析
  • 如何在产品已上线后发现需求遗漏进行补救
  • 重卡充电桩平台支持针对不同车队单独配置计费规则
  • 美丽寮步网站建设高性能广州公关公司有哪些
  • Linux告别搜索卡顿:解决“Argument list too long”与实现文件内容秒搜
  • .NET驾驭Excel之力:工作簿与工作表操作基础
  • 基于 C++ OpenCV 生成小视频
  • 个人网站审批网站防止采集
  • 5.6 Multiple region interfaces
  • 聊聊缓存测试用例设计方案
  • IU5516T低功耗,1M@2.0A降压稳压器
  • Arbess从初级到进阶(3) - 使用Arbess+GitLab+SonarQube搭建Java项目自动化部署
  • 外贸的网站有哪些网站开发心得体会
  • Spring Boot参数校验全流程解析
  • C++进阶:(十)深度解析哈希表:原理、实现与实战
  • 6.3 代码自动生成Agent:程序员的AI副驾驶
  • 最好的网站开发系统网站设计介绍怎么写
  • 如何丰富网站内容在本地搭建wordpress
  • MySQL: 存储引擎深度解析:Memory与Federated的特性与应用场景
  • Java+Leaflet:湖南省道路长度WebGIS的构建与实践
  • 大模型强化学习GRPO-1
  • 网站建设与管理基础带端口的服务器怎么做网站
  • 金融/医疗/教育的第三方软件检测有哪些特别关注点?
  • 文本处理工具:grep、awk、sed 的高级文本分析与处理
  • 【图像处理基石】什么是光流法?
  • Spring事务隔离级别全解析:从读未提交到序列化
  • PostIn从初级到进阶(4) - 如何使用Mock数据尽早满足前后端接口开发需求
  • 建设机械官方网站门源网站建设公司
  • 用 Doris 托底实时明细与聚合Flink CDC Pipeline 的 Doris Sink 实战
  • FLINK CDC 的学习