大家好,我是小冬瓜。随着chatgpt、deepseek等大模型越来越火,企业如何将自身的业务数据和大模型进行结合输出成为很多公司的热门话题。不论是RAG,Function Call还是MCP这些技术很大程度上都是在解决如何在不调节大模型参数的情况下,既能保证数据安全,又能很好的利用大模型的能力进行定制化输出。本文使用spring-ai-mcp框架,用简单的几行代码完整演示下如何将本地私有化数据通过大模型输出,废话不多说直接上代码。
需求目标
通过自然语言将本地私有化数据图表展示。
例:
输入:近7日交易手续费趋势
输出:将本地数据图表形式展示
演示效果
模块介绍
bi: 应用主机,mcp-client
mcp-server-data: 本地mcp服务,主要模拟查询本地私有化数据 http://localhost:8081/sse
mcp-server-chart: 图表mcp服务,在modelscope mcp广场找的服务,可以生成面积、条线图、柱状图、饼图等各种图形的MCP服务(详情),本地化部署 http://localhost:1122/sse,
代码示例
mcp-server-data模块
pom文件
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
配置文件
server:
port: 8081
spring:
ai:
mcp:
server:
name: mcp-server-data
version: 0.0.1
DataService
/**
* @author 任海东
* @since 2025年6月24日
*/
@Service
@Slf4j
public class DataService {
public record IndicatorByDay(String time, double value) {
}
public record ChartData(String type, String axisXTitle, String axisYTitle, List<?> data) {
}
@Tool(description = "获取近N天某指标数据,指标包括交易金额(trade_amount)、交易笔数(trade_count)、交易手续费(fee)等")
public ChartData getLastNDaysIndicator(final int days, final String indicator) {
log.info("查询近{}天的{}数据", days, indicator);
// 模拟查询数据库select dt as time, sum({indicator}) as value from ads_trade_data where
// dt >= date_sub(curdate(), interval {days} day) group by dt
final List<IndicatorByDay> data = List.of(new IndicatorByDay("2025-06-01", 1000.0),
new IndicatorByDay("2025-06-02", 1200.0), new IndicatorByDay("2025-06-03", 900.0),
new IndicatorByDay("2025-06-04", 1100.0), new IndicatorByDay("2025-06-05", 1300.0),
new IndicatorByDay("2025-06-06", 1400.0), new IndicatorByDay("2025-06-07", 1500.0));
return new ChartData("area", "交易日期", indicator, data);
}
@Tool(description = "获取交易排行top10的分公司")
public String getTop10() {
// 模拟返回数据
return "top10";
}
// ...更多查询数据的方法
}
工具注册
@Bean
public ToolCallbackProvider dataTool(final DataService dataService) {
return MethodToolCallbackProvider.builder().toolObjects(dataService).build();
}
bi模块
pom文件
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
配置文件
spring:
application:
name: bi
ai:
openai:
api-key: ${DASHSCOPE_API_KEY}
base-url: https://dashscope.aliyuncs.com/compatible-mode
# base-url: https://localhost:11434
chat:
options:
model: qwen-plus
# model: qwen3:0.6
mcp:
client:
sse:
connections:
chart:
url: http://localhost:1122/sse
data:
url: http://localhost:8081/sse
toolcallback:
enabled: false
logging:
level:
'[io.modelcontextprotocol.client]': debug
'[io.modelcontextprotocol.spec]': debug
这里使用的百炼大模型,开始使用的本地qwen3:0.6b模型,效果不是很好。
代码
/**
* @author 任海东
* @since 2025年6月24日
*/
@SpringBootApplication
@RestController
@AllArgsConstructor
public class BIApplication {
private final List<McpSyncClient> mcpClients;
private final ChatClient.Builder builder;
public static void main(final String[] args) {
SpringApplication.run(BIApplication.class, args);
}
@GetMapping("/bi")
public ResponseEntity<InputStreamResource> generate(
@RequestParam(value = "msg", defaultValue = "近7天交易金额趋势") final String msg) {
final String prompt = """
请根据用户输入信息: %s (e.g. 近7天交易金额趋势)
1. 选择合适的工具查询数据;
2. 将查询的数据作为参数,根据<type>选择合适工具生成图表,并返回图片url;
3. 输出格式:key为url的json字符串。
""".formatted(msg);
final var chatClient = builder.defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpClients)).build();
final String res = chatClient.prompt(prompt).call().content();
final String url = JSON.parseObject(res).getString("url");
Assert.hasLength(url, "未解析出正确的图片");
return url2Resp(url);
}
/**
* 将图片url转换为响应实体
*
* @param url
* @return
*/
private ResponseEntity<InputStreamResource> url2Resp(final String url) {
try {
final URLConnection conn = new URI(url).toURL().openConnection();
return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG)
.body(new InputStreamResource(conn.getInputStream()));
} catch (IOException | URISyntaxException e) {
throw new RuntimeException("系统处理失败: " + e.getMessage(), e);
}
}
}
至此,整个流程完成,客户端不需要手动编排mcp服务的调用链,系统会根据提示词自动处理,是不是超超级简单呢,欢迎关注小冬瓜,一起学下交流!