观察Springboot AI-Function Tools 执行过程

观察Springboot AI-Function Tools 执行过程

Springboot AI-Function Tools 执行流程

构造一个Spring AI MCP Server

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

下载MCP工程后达成JAR,并复制jar的完整路径

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

单元测试

java 复制代码
@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 :哪些字段是必须要传的

json 复制代码
{
    "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,由大模型生成

json 复制代码
{
    "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 就能够进行工具的关联。

json 复制代码
{
    "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": [....]
}

第二次模型返回

json 复制代码
{
    "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依赖

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

之后启动MCP服务

单元测试

java 复制代码
@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 :哪些字段是必须要传的

json 复制代码
{
    "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,由大模型生成

json 复制代码
{
    "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** 就能够进行工具的关联。

json 复制代码
{
    "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"
                    ]
                }
            }
        }
    ]
}

第二次模型响应

json 复制代码
{
    "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依赖

xml 复制代码
<!-- 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>
相关推荐
韩立学长7 分钟前
基于Springboot课堂教学辅助系统08922bq1(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
Dev7z16 分钟前
基于Matlab卷积神经网络的交通警察手势识别方法研究与实现
人工智能·神经网络·cnn
盖世英雄酱5813627 分钟前
java深度调试技术【第六七八章:宽字节与多字节】
java·后端
元拓数智42 分钟前
IntaLink:破解数仓建设痛点,重塑高效建设新范式
大数据·数据仓库·人工智能·数据关系·intalink
区块链小八歌1 小时前
从电商收入到链上资产:Liquid Royalty在 Berachain 重塑 RWA 想象力
大数据·人工智能·区块链
沃达德软件1 小时前
大数据反诈平台功能解析
大数据·人工智能
OAoffice1 小时前
智能学习培训考试平台如何驱动未来组织:重塑人才发展格局
人工智能·学习·企业智能学习考试平台·学练考一体化平台
岁月宁静1 小时前
LangChain + LangGraph 实战:构建生产级多模态 WorkflowAgent 的完整指南
人工智能·python·agent
Java中文社群1 小时前
重磅!N8N新版2.0发布!不再支持MySQL?
人工智能