观察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>
相关推荐
weixin_4374977714 小时前
读书笔记:Context Engineering 2.0 (上)
人工智能·nlp
喝拿铁写前端14 小时前
前端开发者使用 AI 的能力层级——从表面使用到工程化能力的真正分水岭
前端·人工智能·程序员
goodfat14 小时前
Win11如何关闭自动更新 Win11暂停系统更新的设置方法【教程】
人工智能·禁止windows更新·win11优化工具
北京领雁科技14 小时前
领雁科技反洗钱案例白皮书暨人工智能在反洗钱系统中的深度应用
人工智能·科技·安全
落叶,听雪14 小时前
河南建站系统哪个好
大数据·人工智能·python
上进小菜猪14 小时前
基于 YOLOv8 的驾驶员疲劳状态识别系统实战(含完整源码与可视化界面)
后端
清月电子14 小时前
杰理AC109N系列AC1082 AC1074 AC1090 芯片停产替代及资料说明
人工智能·单片机·嵌入式硬件·物联网
Dev7z14 小时前
非线性MPC在自动驾驶路径跟踪与避障控制中的应用及Matlab实现
人工智能·matlab·自动驾驶
上进小菜猪14 小时前
基于 YOLOv8 的交通标识与设施识别系统(含完整源码)
后端
七月shi人14 小时前
AI浪潮下,前端路在何方
前端·人工智能·ai编程