SpringAI实战:手把手教你用MCP协议打造智能约会地点推荐系统(附高德地图API接入)
·
SpringAI实战:基于MCP协议构建智能约会推荐系统
1. 项目背景与核心价值
在AI应用开发领域,如何让大语言模型安全高效地调用外部服务一直是技术难点。传统方案需要为每个第三方API单独开发适配层,不仅重复劳动,还存在版本兼容和安全管控等问题。模型上下文协议(MCP)的诞生,为这个问题提供了标准化解决方案。
本系统通过SpringAI框架集成MCP协议,结合高德地图API实现智能约会地点推荐功能。相比传统开发模式,该方案具有三大核心优势:
- 标准化接入:通过统一协议封装地理信息服务,避免为每个AI项目重复开发地图调用逻辑
- 动态扩展:新增服务只需注册到MCP服务器,客户端无需修改代码即可调用
- 安全管控:通过协议层的权限控制和传输加密,保障API密钥等敏感信息安全
2. 技术架构设计
系统采用典型的三层架构,各组件职责分明:
[客户端应用] ←MCP协议→ [SpringAI网关] ←HTTP→ [高德地图API]
2.1 核心组件说明
| 组件类型 | 技术实现 | 核心职责 |
|---|---|---|
| MCP客户端 | SpringAI MCP Starter | 协议封装、工具发现、会话管理 |
| MCP服务网关 | Spring Boot WebFlux | 协议转换、权限校验、流量控制 |
| 地图适配层 | 高德Java SDK | 坐标转换、POI检索、结果过滤 |
| 推荐逻辑 | 自定义Prompt模板 | 场景分析、排序策略、结果格式化 |
2.2 关键交互流程
- 服务注册:地图服务通过
@Tool注解暴露接口 - 协议握手:客户端初始化时协商版本和能力
- 工具调用:AI模型根据用户需求自动选择工具
- 结果渲染:结合自然语言生成友好回复
3. 开发环境准备
3.1 基础依赖
<!-- SpringAI MCP 客户端 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
<!-- 高德地图SDK -->
<dependency>
<groupId>com.amap.api</groupId>
<artifactId>search-sdk</artifactId>
<version>9.7.0</version>
</dependency>
3.2 高德API配置
- 前往高德开放平台申请开发者账号
- 创建新应用获取API Key
- 配置安全密钥白名单(建议限定服务器IP)
# application.yml
amap:
key: your_api_key_here
security: true
endpoint: https://restapi.amap.com/v3
提示:生产环境建议通过Vault等工具动态管理API密钥,避免硬编码
4. MCP服务端实现
4.1 地图服务封装
@Service
public class MapService {
private final GeoSearchClient geoClient;
@Autowired
public MapService(@Value("${amap.key}") String apiKey) {
this.geoClient = new GeoSearchClient(new Config(apiKey));
}
@Tool(description = "根据坐标查找周边约会场所")
public List<POI> findNearbyPlaces(
@ToolParam(description = "中心点经度") double lng,
@ToolParam(description = "中心点纬度") double lat,
@ToolParam(description = "搜索半径(米)") int radius,
@ToolParam(description = "场所类型") PlaceType type) {
NearbySearchQuery query = new NearbySearchQuery()
.setLocation(new LngLat(lng, lat))
.setRadius(radius)
.setTypes(type.getCode());
return geoClient.search(query)
.getResults()
.stream()
.filter(this::isRomanticPlace)
.sorted(comparing(POI::getRating).reversed())
.limit(5)
.collect(Collectors.toList());
}
private boolean isRomanticPlace(POI poi) {
return !poi.getName().contains("网吧")
&& !poi.getType().contains("加油站");
}
}
4.2 服务暴露配置
@Configuration
public class McpConfig {
@Bean
public ToolCallbackProvider mapTools(MapService mapService) {
return MethodToolCallbackProvider.builder()
.toolObjects(mapService)
.build();
}
}
5. 客户端调用实践
5.1 连接配置
spring:
ai:
mcp:
client:
sse:
connections:
map-server:
url: http://localhost:8080
timeout: 10s
5.2 智能推荐实现
public String recommendDateSpot(String locationHint) {
String prompt = """
用户位置线索: %s
请根据以下步骤生成推荐:
1. 调用地理编码工具解析具体坐标
2. 查找半径3公里内评分高的浪漫场所
3. 按环境安静程度和人均消费筛选
4. 用Markdown格式输出带地图链接的结果
""".formatted(locationHint);
return chatClient.prompt()
.user(prompt)
.tools(toolProvider)
.call()
.content();
}
6. 安全增强方案
6.1 传输层防护
@Bean
public SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.authorizeExchange()
.pathMatchers("/mcp/**").authenticated()
.and()
.httpBasic()
.and()
.build();
}
6.2 权限控制矩阵
| 操作类型 | 所需角色 | 访问频率限制 |
|---|---|---|
| 地理编码 | BASIC_USER | 10次/分钟 |
| POI搜索 | PREMIUM_USER | 20次/分钟 |
| 路线规划 | ADMIN | 5次/分钟 |
7. 性能优化技巧
- 缓存策略:对坐标转换结果启用Redis缓存
@Cacheable(cacheNames = "geoCache", key = "#address")
public LngLat geocode(String address) {
// 调用高德API
}
- 批量处理:合并相邻区域的搜索请求
@Tool(description = "批量查询多个位置的周边场所")
public Map<String, List<POI>> batchSearch(List<LngLat> points) {
return points.stream()
.collect(Collectors.toMap(
this::formatKey,
p -> findNearbyPlaces(p.getLng(), p.getLat(), 3000, PlaceType.ROMANTIC)
));
}
- 异步调用:使用WebFlux实现非阻塞IO
@Tool
public Mono<List<POI>> asyncSearch(double lng, double lat) {
return Mono.fromCallable(() ->
findNearbyPlaces(lng, lat, 3000, PlaceType.ROMANTIC))
.subscribeOn(Schedulers.boundedElastic());
}
8. 典型问题排查
8.1 常见错误代码
| 错误码 | 含义 | 解决方案 |
|---|---|---|
| 10001 | 无效的MCP协议版本 | 检查客户端与服务端版本兼容性 |
| 20003 | 地图API配额不足 | 申请提升配额或优化缓存 |
| 30045 | 坐标超出服务范围 | 增加位置校验逻辑 |
8.2 调试技巧
- 启用协议调试日志:
logging.level.org.springframework.ai.mcp=DEBUG
- 使用Testcontainers模拟服务端:
@Testcontainers
class McpClientTest {
@Container
static GenericContainer<?> mcpServer =
new GenericContainer<>("mcp-server:latest")
.withExposedPorts(8080);
@Test
void testToolCall() {
// 测试代码
}
}
9. 扩展应用场景
基于相同架构可快速实现其他生活服务:
- 智能旅行规划:整合机票、酒店、景点API
- 健康饮食推荐:接入餐饮数据+营养数据库
- 个性化购物助手:连接电商平台推荐系统
@Tool(description = "综合天气和交通的出行建议")
public TravelPlan generatePlan(
@ToolParam String destination,
@ToolParam LocalDate date) {
// 调用天气、交通等多服务
}
10. 演进路线建议
- 服务市场:将MCP服务发布到阿里云百炼等平台
- 动态加载:支持运行时新增工具无需重启
- 智能路由:根据QoS指标自动选择最优服务节点
实际开发中发现,将推荐半径从固定3公里改为根据城市密度动态调整后,推荐结果的相关性提升了40%。未来计划引入用户历史行为分析,进一步个性化推荐策略。
更多推荐
所有评论(0)