1. 项目概述
在Spring Boot生态系统中集成AI能力已经成为现代企业应用开发的重要趋势。本文将详细介绍如何在SpringBoot-AI框架中调用MCP工具实现AI交互的完整流程。这个技术方案特别适合需要将企业现有业务系统与AI能力相结合的后端开发场景。
通过这个案例,我们将实现一个员工薪资查询功能:用户用自然语言提出查询请求,AI理解意图后调用MCP工具获取具体数据,最后将结构化结果转化为自然语言回复。整个过程涉及SpringBoot-AI框架配置、MCP工具集成、大模型交互协议等关键技术点。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与工具配置
2.1 基础环境搭建
首先需要准备以下开发环境:
- JDK 17或更高版本
- Maven 3.8+或Gradle 7.x
- Spring Boot 3.1.0+
- spring-ai 1.0.0框架
在pom.xml中添加必要的依赖:
xml复制<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>mcp-client-sdk</artifactId>
<version>1.2.0</version>
</dependency>
2.2 MCP客户端初始化
MCP(Message Control Protocol)是企业内部常用的服务调用协议。我们需要先初始化MCP客户端:
java复制@Bean
public McpSyncClient sseMcpClient() {
HttpClientSseClientTransport sseClientTransport = HttpClientSseClientTransport
.builder("http://localhost:9102/sse")
.connectTimeout(Duration.ofSeconds(30))
.readTimeout(Duration.ofMinutes(5))
.build();
McpSyncClient mcpSyncClient = McpClient.sync(sseClientTransport)
.requestTimeout(Duration.ofMinutes(360))
.build();
var initResponse = mcpSyncClient.initialize();
log.info("MCP Client Initialized: {}", initResponse);
return mcpSyncClient;
}
关键配置说明:
connectTimeout:建立连接的超时时间,建议30秒readTimeout:读取响应的超时时间,根据业务需求设置requestTimeout:整个请求的超时时间,长任务需要设置较大值
注意:生产环境建议将MCP服务地址配置在application.yml中,而不是硬编码在代码里
3. AI交互流程实现
3.1 构建ChatClient
创建配置类设置ChatClient:
java复制@Configuration
public class AIConfig {
@Autowired
private McpSyncClient mcpSyncClient;
@Bean
public ChatClient chatClient(ChatClient.Builder chatClientBuilder) {
return chatClientBuilder.defaultOptions(
OpenAiChatOptions.builder()
.model("deepseek-chat")
.temperature(0.7)
.toolCallbacks(new SyncMcpToolCallbackProvider(mcpSyncClient).getToolCallbacks())
.build())
.build();
}
}
参数说明:
model:指定使用的大模型temperature:控制生成结果的随机性,0.7是平衡值toolCallbacks:注册MCP工具回调处理器
3.2 实现工具调用
创建测试用例验证完整流程:
java复制@SpringBootTest
public class EmployeeSalaryTest {
@Autowired
private ChatClient chatClient;
@Test
public void testSalaryQuery() {
String response = chatClient.prompt("查看一下员工[蒋颜刚]的薪水")
.call()
.content();
log.info("查询结果: {}", response);
}
}
执行流程说明:
- 用户输入自然语言查询
- AI识别意图并决定调用MCP工具
- 获取工具结果后生成自然语言回复
4. 网络交互协议解析
4.1 第一次请求/响应
请求报文:
json复制{
"messages": [{
"content": "查看一下员工[蒋颜刚]的薪水",
"role": "user"
}],
"model": "deepseek-chat",
"tools": [{
"type": "function",
"function": {
"name": "JavaSDKMCPClient_getSalaryDetailInfo",
"parameters": {
"type": "object",
"properties": {
"request": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "员工名字"
}
},
"required": ["name"]
}
},
"required": ["request"]
}
}
}]
}
响应报文关键字段:
json复制{
"choices": [{
"message": {
"tool_calls": [{
"id": "call_00_gk4tILLa2vCVfAZaKST24GNd",
"function": {
"name": "JavaSDKMCPClient_getSalaryDetailInfo",
"arguments": "{\"request\": {\"name\": \"蒋颜刚\"}}"
}
}]
},
"finish_reason": "tool_calls"
}]
}
协议要点:
finish_reason: tool_calls表示需要调用工具tool_calls包含要调用的函数名和参数
4.2 第二次请求/响应
请求报文变化:
json复制{
"messages": [
// 原始用户消息
{
"content": "查看一下员工[蒋颜刚]的薪水",
"role": "user"
},
// AI的第一次回复
{
"role": "assistant",
"tool_calls": [{
"id": "call_00_gk4tILLa2vCVfAZaKST24GNd",
"function": {
"name": "JavaSDKMCPClient_getSalaryDetailInfo",
"arguments": "{\"request\": {\"name\": \"蒋颜刚\"}}"
}
}]
},
// 工具返回结果
{
"role": "tool",
"name": "JavaSDKMCPClient_getSalaryDetailInfo",
"content": "[{\"text\":\"{\\\"salary\\\":20000}\"}]",
"tool_call_id": "call_00_gk4tILLa2vCVfAZaKST24GNd"
}
]
}
最终响应:
json复制{
"choices": [{
"message": {
"content": "员工蒋颜刚的月薪为20,000元"
},
"finish_reason": "stop"
}]
}
协议要点:
role: tool表示工具返回的结果tool_call_id关联工具调用和结果finish_reason: stop表示对话结束
5. 核心源码解析
5.1 工具调用触发机制
在OpenAiChatModel.internalCall()方法中:
java复制if (this.toolExecutionEligibilityPredicate.isToolExecutionRequired(
prompt.getOptions(), response)) {
// 执行工具调用
var toolExecutionResult = this.toolCallingManager.executeToolCalls(prompt, response);
if (toolExecutionResult.returnDirect()) {
// 直接返回工具结果
return ChatResponse.builder()
.from(response)
.generations(ToolExecutionResult.buildGenerations(toolExecutionResult))
.build();
} else {
// 将工具结果送回大模型
return this.internalCall(
new Prompt(toolExecutionResult.conversationHistory(), prompt.getOptions()),
response);
}
}
关键逻辑:
- 检查响应是否需要工具调用
- 执行工具并获取结果
- 根据配置决定直接返回或继续对话
5.2 消息历史管理
工具调用过程中,消息历史的构建是关键:
java复制public class ToolExecutionResult {
private final List<Message> conversationHistory;
public static List<Generation> buildGenerations(ToolExecutionResult result) {
return result.conversationHistory().stream()
.map(message -> new Generation(message.content()))
.toList();
}
}
消息历史包含:
- 原始用户消息
- AI的工具调用请求
- 工具返回结果
6. 生产环境注意事项
6.1 性能优化建议
- 连接池配置:
java复制@Bean
public HttpClient httpClient() {
return HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000)
.responseTimeout(Duration.ofSeconds(30))
.doOnConnected(conn ->
conn.addHandlerLast(new ReadTimeoutHandler(60, TimeUnit.SECONDS)));
}
- 缓存策略:
- 对频繁查询的员工信息实现本地缓存
- 考虑使用Caffeine或Redis
- 超时设置:
- MCP调用超时应该短于AI交互超时
- 设置合理的重试机制
6.2 错误处理
完善错误处理机制:
java复制@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultOptions(/*...*/)
.defaultAdvisors(
new RetryAdvisor(3, Duration.ofSeconds(1)),
new CircuitBreakerAdvisor(5, Duration.ofMinutes(1))
)
.build();
}
常见错误场景:
- MCP服务不可用
- 大模型响应超时
- 工具调用参数错误
6.3 安全考虑
- 输入验证:
java复制@PreAuthorize("#name.matches('[\\u4e00-\\u9fa5]{2,5}')")
public String querySalary(String name) {
// ...
}
- 敏感数据脱敏:
java复制public String maskSalaryInfo(String original) {
return original.replaceAll("(\"salary\":\\s*)\\d+", "$1****");
}
- 访问日志审计:
java复制@Around("execution(* com.example..*.*(..))")
public Object audit(ProceedingJoinPoint pjp) {
// 记录方法调用和参数
}
7. 扩展应用场景
7.1 多工具组合调用
实现多个MCP工具的组合调用:
java复制public class MultiToolCallbackProvider implements ToolCallbackProvider {
private final List<ToolCallback> callbacks;
public MultiToolCallbackProvider(McpSyncClient mcpClient) {
this.callbacks = List.of(
new SalaryToolCallback(mcpClient),
new DepartmentToolCallback(mcpClient),
new AttendanceToolCallback(mcpClient)
);
}
@Override
public List<ToolCallback> getToolCallbacks() {
return callbacks;
}
}
7.2 流式响应处理
支持流式响应:
java复制chatClient.prompt("查询蒋颜刚的薪资和考勤")
.stream()
.subscribe(chunk -> {
System.out.print(chunk.getContent());
});
7.3 自定义工具描述
优化工具描述提升大模型理解:
java复制@FunctionDescription(
name = "JavaSDKMCPClient_getSalaryDetailInfo",
description = "获取员工薪资详细信息,包括基本工资、补贴和扣款项",
parameters = @Parameters({
@Parameter(name = "request", description = "查询请求", required = true,
attributes = {
@Attribute(name = "name", type = "string", description = "员工中文姓名,2-4个汉字"),
@Attribute(name = "month", type = "string", description = "查询月份,格式YYYY-MM")
})
})
)
public String getSalaryDetailInfo(Map<String, Object> params) {
// ...
}
8. 调试与问题排查
8.1 常见问题解决
- 工具未触发:
- 检查工具描述是否完整
- 验证大模型是否支持工具调用
- 检查finish_reason是否为tool_calls
- 参数解析失败:
- 确保参数JSON格式正确
- 验证参数类型与描述一致
- 响应超时:
- 检查MCP服务状态
- 调整超时设置
- 考虑异步调用模式
8.2 调试技巧
- 使用Charles抓包分析:
- 过滤特定域名
- 检查请求/响应内容
- 日志配置:
properties复制logging.level.org.springframework.ai=DEBUG
logging.level.com.example.mcp=TRACE
- 单元测试策略:
java复制@Test
public void testToolInvocation() {
// 模拟工具调用
when(mcpClient.execute(any())).thenReturn(mockResponse);
// 验证交互流程
String result = chatClient.prompt("...").call().content();
assertThat(result).contains("20,000");
}
9. 性能监控与优化
9.1 监控指标
关键监控指标:
- 大模型响应时间
- MCP工具调用耗时
- 整体请求成功率
- 令牌使用情况
9.2 Prometheus配置
示例监控配置:
yaml复制metrics:
enabled: true
prometheus:
enabled: true
endpoint: /actuator/prometheus
自定义指标:
java复制@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags(
"application", "springboot-ai-demo",
"region", "china-east"
);
}
9.3 性能优化策略
- 批量查询:
java复制public List<SalaryInfo> batchQuery(List<String> names) {
// 实现批量查询接口
}
- 结果缓存:
java复制@Cacheable(value = "salary", key = "#name")
public SalaryInfo querySalary(String name) {
// ...
}
- 异步处理:
java复制@Async
public CompletableFuture<String> asyncQuery(String name) {
// ...
}
10. 架构设计思考
10.1 分层架构建议
推荐的分层结构:
code复制└── 应用层
├── API接口
├── 业务逻辑
└── DTO转换
└── 领域层
├── 领域服务
└── 领域模型
└── 基础设施层
├── MCP客户端
├── AI集成
└── 持久化
10.2 扩展性设计
- 工具注册中心:
java复制public interface ToolRegistry {
void register(ToolCallback callback);
List<ToolCallback> getCallbacks();
}
- 插件化架构:
java复制public interface AIPlugin {
String getName();
void configure(ChatClient.Builder builder);
}
- 动态配置:
java复制@RefreshScope
@Bean
public ChatClient chatClient(
@Value("${ai.model}") String model,
@Value("${ai.temperature}") float temperature) {
// ...
}
10.3 容错设计
- 降级策略:
java复制@Fallback(fallbackMethod = "getSalaryFallback")
public SalaryInfo getSalary(String name) {
// ...
}
public SalaryInfo getSalaryFallback(String name) {
return new SalaryInfo(name, 0, "数据暂不可用");
}
- 熔断配置:
properties复制resilience4j.circuitbreaker.instances.mcp.failure-rate-threshold=50
resilience4j.circuitbreaker.instances.mcp.wait-duration-in-open-state=5000
- 限流保护:
java复制@RateLimiter(name = "salaryQuery")
public SalaryInfo query(String name) {
// ...
}
在实际项目中,这种AI与业务系统集成的架构可以扩展到各种场景,如客户服务、数据分析、智能审批等。关键在于设计良好的工具接口和清晰的交互协议,确保AI能力能够无缝融入现有业务流程。
