千问通义plus - MCP添加进智能体(高德地图)
模型上下文协议(MCP)是一种比传统函数调用更灵活的大模型扩展方案。文章通过银行填表和客服对话的比喻,说明MCP支持自然语言交互,无需严格定义格式,使大模型能更自然地调用外部工具。具体实现上,以高德地图API为例,展示了如何注册开发者账号、创建应用、获取API Key,并将MCP接入智能体。文章还提供了Spring Boot集成MCP的代码修改示例,包括新增appId配置和在请求头中传递参数的方法
一、介绍
官方文档:模型上下文协议(Model Context Protocol, MCP)可帮助大模型使用外部工具与数据,相比 Function Calling,MCP 更灵活且易于使用。
咱们用生活化的比喻,一分钟讲明白:
1. 核心定位
MCP(模型上下文协议)和 Function Calling(函数调用)做的是同一件事:帮大模型 “借力”—— 调用外部工具(比如查天气、算数据、查数据库)、访问外部数据,弥补大模型自身的不足(比如不会实时查数据、算复杂公式)。
2. 关键差异:Function Calling vs MCP
1. Function Calling(函数调用):“格式化填表”
就像你去银行办业务,必须填固定格式的申请表:
- 要调用工具,得先严格定义好 “函数名、参数格式、返回格式”(比如必须写
get_weather(city="北京"),少一个引号、错一个参数名就不行); - 交互方式固定:大模型发一次调用请求,工具返回一次结果,流程死板;
- 开发 / 使用门槛高:得专门写 “格式说明书(Schema)”,新手容易卡格式错误。
2. MCP(模型上下文协议):“自然对话”
就像你跟客服说话,不用填表单,直接说需求就行:
- 不用严格定义格式,大模型可以用自然语言和工具 “沟通”(比如直接说 “帮我查下北京今天的天气”,工具就能理解);
- 交互更灵活:工具返回结果后,大模型还能追问、调整需求(比如 “再查下上海的,对比下温度”),不用重新走一遍固定流程;
- 易用性高:开发人员不用花大量时间写格式说明书,工具接入更简单,大模型使用起来也更顺滑。
3.总结
两者都能帮大模型用外部工具,但 Function Calling 是 “按固定表单办事”,MCP 是 “按自然对话办事”,所以 MCP 更灵活(适配更多场景)、更好用(门槛低、少踩格式坑)。
二、实现
1. 获取MCP
想要获取MCP,我们需要考虑自己要什么功能?
这里我想要实现对目的地线路的规划,那么毫无疑问,我需要获得地图方面的支持,那么就是你了!上把高德地图!!
开发指南-Web服务 API | 高德地图API 通过该链接进入高德地图开放平台

有账号的就登录,没有的就注册一个然后注册一下开发者

接着创建一个应用

这里我们主要做线路的规划,所以应用类型我们就选择出行

添加key

命名随意,当然建议你按照官方给的规则 [应用名 + 应用场景]
然后选择web服务平台,下面就会出现你可以使用的各种服务

确定后就确定好了你的MCP了

接下来我们就可以复制我们的key开始使用了,你可以去控制台查看你的额度,正常开发的话基本是够用的。

2. 部署MCP
官方文档 大模型服务平台百炼控制台 大家可以看看,接下来是我的操作,目前部署的这个也是限时free的,大家可以去试一试,后续就算收费也是几分钱甚至更好,在账号里面充个十块钱,方便我们后续学习。
1.搜索 Amap Maps

2.开通服务并输入我们的key

3.将MCP添加到单智能体中

然后就会自动跳转到我们的智能体应用当中了,这个时候下滑就可以看到高德地图的所有MCP都已经接入到了我们的智能体之中了,之后就发布就可以了。

3.代码修改
基于上一文章 千问通义plus - 代码解释器的使用-CSDN博客 一文,我来做一下代码的修改
1.新增appId
在类中新增 appid 的配置注入(与现有 apiKey/model 同级):
@Value("${spring.ai.dashscope.agent.options.app-id}")
private String appId;
2.在 GenerationParam 中传递 appid
GenerationParam param = GenerationParam.builder()
.model(model) // 如 qwen-plus
.apiKey(apiKey)
.messages(messages)
.tools(Collections.unmodifiableList(toolFunctions)) // 替换为包含Java工具的列表
.enableSearch(true) // 启用联网搜索
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.incrementalOutput(true) // 流式输出
.headers(Collections.singletonMap("X-DashScope-AppID", appId))
.build();
3.完整代码
// 省略原有无关代码,仅展示修改部分
@Slf4j
@Service
@RequiredArgsConstructor
public class DashScopeServiceImpl implements DashScopeService {
// 缓存工具类(原有)
private final ChatContextCacheUtil chatContextCacheUtil;
// 通义千问配置(原有 + 新增 appid)
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
@Value("${spring.ai.dashscope.model}")
private String model;
@Value("${spring.ai.dashscope.app-id}") // 新增 appid 配置
private String appId;
// 聚合数据天气接口配置(原有)
@Value("${jute.weather.api-key}")
private String juheWeatherApiKey;
// 其他原有配置...
@Override
public SseEmitter streamChat(ChatRequestDTO requestDTO, HttpServletResponse response) {
// 原有逻辑...
// 构建 GenerationParam 时添加 headers 传递 appid
GenerationParam param = GenerationParam.builder()
.model(model)
.apiKey(apiKey)
.messages(messages)
.tools(Collections.unmodifiableList(toolFunctions))
.enableSearch(true)
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.incrementalOutput(true)
.headers(Collections.singletonMap("X-DashScope-AppID", appId)) // 新增
.build();
// 原有逻辑...
}
// 其他原有方法保持不变...
}
4.结果


这里的数据都变成一个类被输出了,估计是因为前面的代码解释器没有做好内容鉴别,这个需要在智能体中写好逻辑判断的话语,后续我会修改好代码,或者大家可以使用我 Springboot调用单智能体-CSDN博客 这一版的代码。
如果你有任何疑问都可以私信或者评论区留言,如果你觉得我写的还行,可以点点关注
更多推荐


所有评论(0)