MCP 系列五:编程实战,手把手教你实现数学运算智能问答应用
本文介绍如何整合前文实现的 MCP 服务端和 MCP 客户端(基于 SSE 传输协议),实现数学运算智能问答应用
本文为 MCP 系列的第五篇文章。前文我们分别给出了服务端、客户端的编程实现,
本文介绍如何整合前文实现的 MCP 服务端和 MCP 客户端(基于 SSE 传输协议),实现数学运算智能问答应用,该应用可实现以下功能:
-
用户与大模型的交互式对话。
-
列出 MCP 服务端可用的全部工具 tool 的信息。
-
大模型根据用户的问题,判断是否要调用工具。若要调用工具,大模型给出工具调用的请求参数。
-
通过 MCP 客户端,对 MCP 服务端进行工具调用请求,并获取工具调用结果。
-
大模型结合工具调用结果,回答用户的问题。
本文的主要内容如下:
-
主要对象关系、应用实现流程步骤
-
系统提示词(应用的关键,指导 LLM 如何与 MCP 服务进行交互)
-
编码实现
-
效果验证
笔者在 Ollama 系列文章(Ollama-python:工具赋能大模型,以数学运算智能问答应用为例),详细介绍了如何基于 Function Call 实现数学运算智能问答应用。读者可以对比阅读,比较 MCP 与 Function Call 两者的特点。
关注我,后台回复 MCP,可获取本文的具体代码。
主要对象关系、应用实现流程步骤
主要对象关系
-
会话管理:
-
处理用户输入并展示AI响应
-
维护对话历史记录
-
协调LLM和MCP客户端之间的交互
-
解析LLM响应中的工具调用请求
-
处理工具执行结果并将其反馈给LLM
-
-
模型客户端:与大语言模型API通信的客户端。主要职责:封装与 LLM 服务的API交互;发送对话历史到LLM并获取响应。
-
MCP客户端:与MCP服务端通信的客户端
-
MCP服务端:提供工具服务的后端服务器
流程步骤
-
初始化阶段 - 建立MCP连接并获取工具列表
-
交互循环 - 用户输入,LLM响应,工具调用处理
-
工具调用处理 - 当LLM返回工具调用指令时的处理流程
-
清理资源 - 程序退出时的资源清理
系统提示词
系统提示词是整个应用的关键部分,它指导 LLM 如何与 MCP 服务进行交互。以下是经过笔者针对本应用的经过多次调试的系统提示词:
# 系统提示,指导LLM如何使用工具和返回响应
system_message = f'''
你是一个智能助手,严格遵循以下协议返回响应:
可用工具:{tools_description}
响应规则:
1、当需要计算时,返回严格符合以下格式的纯净JSON:
{{
"tool": "tool-name",
"arguments": {{
"argument-name": "value"
}}
}}
2、禁止包含以下内容:
- Markdown标记(如```json)
- 自然语言解释(如"结果:")
- 格式化数值(必须保持原始精度)
- 单位符号(如元、kg)
校验流程:
✓ 参数数量与工具定义一致
✓ 数值类型为number
✓ JSON格式有效性检查
正确示例:
用户:单价88.5买235个多少钱?
响应:{{"tool":"multiply","arguments":{{"a":88.5,"b":235}}}}
错误示例:
用户:总金额是多少?
错误响应:总价500元 → 含自然语言
错误响应:```json{{...}}``` → 含Markdown
3、在收到工具的响应后:
- 将原始数据转化为自然、对话式的回应
- 保持回复简洁但信息丰富
- 聚焦于最相关的信息
- 使用用户问题中的适当上下文
- 避免简单重复使用原始数据
'''
编码实现
LLMClient
class LLMClient:
"""LLM客户端,负责与大语言模型API通信"""
def __init__(self, model_name: str, url: str, api_key: str) -> None:
self.model_name: str = model_name
self.url: str = url
self.client = OpenAI(api_key=api_key, base_url=url)
def get_response(self, messages: list[dict[str, str]]) -> str:
"""发送消息给LLM并获取响应"""
response = self.client.chat.completions.create(
model=self.model_name,
messages=messages,
stream=False
)
return response.choices[0].message.content
ChatSession:
class ChatSession:
"""聊天会话,处理用户输入和LLM响应,并与MCP工具交互"""
def __init__(self, llm_client: LLMClient, mcp_client: MCPClient, ) -> None:
self.mcp_client: MCPClient = mcp_client
self.llm_client: LLMClient = llm_client
asyncdef cleanup(self) -> None:
"""清理MCP客户端资源"""
try:
await self.mcp_client.cleanup()
except Exception as e:
logging.warning(f"Warning during final cleanup: {e}")
asyncdef process_llm_response(self, llm_response: str) -> str:
"""处理LLM响应,解析工具调用并执行"""
try:
# 尝试移除可能的markdown格式
if llm_response.startswith('```json'):
llm_response = llm_response.strip('```json').strip('```').strip()
tool_call = json.loads(llm_response)
if"tool"in tool_call and"arguments"in tool_call:
# 检查工具是否可用
tools = await self.mcp_client.list_tools()
if any(tool.name == tool_call["tool"] for tool in tools):
try:
# 执行工具调用
result = await self.mcp_client.execute_tool(
tool_call["tool"], tool_call["arguments"]
)
returnf"Tool execution result: {result}"
except Exception as e:
error_msg = f"Error executing tool: {str(e)}"
logging.error(error_msg)
return error_msg
returnf"No server found with tool: {tool_call['tool']}"
return llm_response
except json.JSONDecodeError:
# 如果不是JSON格式,直接返回原始响应
return llm_response
asyncdef start(self, system_message) -> None:
"""启动聊天会话的主循环"""
messages = [{"role": "system", "content": system_message}]
whileTrue:
try:
# 获取用户输入
user_input = input("用户: ").strip().lower()
if user_input in ["quit", "exit", "退出"]:
print('AI助手退出')
break
messages.append({"role": "user", "content": user_input})
# 获取LLM的初始响应
llm_response = self.llm_client.get_response(messages)
print("助手: ", llm_response)
# 处理可能的工具调用
result = await self.process_llm_response(llm_response)
# 如果处理结果与原始响应不同,说明执行了工具调用,需要进一步处理
while result != llm_response:
messages.append({"role": "assistant", "content": llm_response})
messages.append({"role": "system", "content": result})
# 将工具执行结果发送回LLM获取新响应
llm_response = self.llm_client.get_response(messages)
result = await self.process_llm_response(llm_response)
print("助手: ", llm_response)
messages.append({"role": "assistant", "content": llm_response})
except KeyboardInterrupt:
print('AI助手退出')
break
main 函数:
async def main():
"""主函数,初始化客户端并启动聊天会话"""
mcp_client = MCPClient()
# 初始化LLM客户端,使用通义千问模型
llm_client = LLMClient(model_name='qwen-plus-latest', api_key=os.getenv('DASHSCOPE_API_KEY'),
url='https://dashscope.aliyuncs.com/compatible-mode/v1')
try:
# 连接到MCP服务器
await mcp_client.connect_to_sse_server('http://localhost:8080/sse')
chat_session = ChatSession(llm_client=llm_client, mcp_client=mcp_client)
# 获取可用工具列表并格式化为系统提示的一部分
tools = await mcp_client.list_tools()
dict_list = [tool.__dict__ for tool in tools]
tools_description = json.dumps(dict_list, ensure_ascii=False)
# 系统提示,指导LLM如何使用工具和返回响应
system_message = f'''
你是一个智能助手,严格遵循以下协议返回响应:
可用工具:{tools_description}
响应规则:
1、当需要计算时,返回严格符合以下格式的纯净JSON:
{{
"tool": "tool-name",
"arguments": {{
"argument-name": "value"
}}
}}
2、禁止包含以下内容:
- Markdown标记(如```json)
- 自然语言解释(如"结果:")
- 格式化数值(必须保持原始精度)
- 单位符号(如元、kg)
校验流程:
✓ 参数数量与工具定义一致
✓ 数值类型为number
✓ JSON格式有效性检查
正确示例:
用户:单价88.5买235个多少钱?
响应:{{"tool":"multiply","arguments":{{"a":88.5,"b":235}}}}
错误示例:
用户:总金额是多少?
错误响应:总价500元 → 含自然语言
错误响应:```json{{...}}``` → 含Markdown
3、在收到工具的响应后:
- 将原始数据转化为自然、对话式的回应
- 保持回复简洁但信息丰富
- 聚焦于最相关的信息
- 使用用户问题中的适当上下文
- 避免简单重复使用原始数据
'''
# 启动聊天会话
await chat_session.start(system_message)
finally:
# 确保资源被清理
await chat_session.cleanup()
if __name__ == "__main__":
asyncio.run(main())
效果验证
-
运行 mcp 服务端(mcp 服务端,可阅读前文:)
-
本应用运行效果:
模型可以根据用户的问题,正确地判断并调用工具,根据工具的调用结果,回答用户的问题:
D:\python_project\mcp_learning\.venv\Scripts\python.exe D:\python_project\mcp_learning\client\math_compute.py
用户: 现在要购买一批货,单价是 1034.32423,数量是 235326。商家后来又说,可以在这个基础上,打95折,折后总价是多少?
助手: {
"tool": "multiply",
"arguments": {
"a": 1034.32423,
"b": 235326
}
}
助手: {
"tool": "multiply",
"arguments": {
"a": 243403383.74898,
"b": 0.95
}
}
助手: 折后总价是231233214.56153098。
用户: 我和商家关系比较好,商家说,可以在上面的基础上,再返回两个点,最后总价是多少?
助手: {
"tool": "multiply",
"arguments": {
"a": 231233214.56153098,
"b": 0.98
}
}
助手: 最终总价是226608550.27030036。
用户: quit
AI助手退出
以上是基于 MCP 实现的数学运算智能问答应用的编程实现,欢迎关注我,后续介绍更多 MCP 的相关内容。
如何系统的去学习大模型LLM ?
大模型时代,火爆出圈的LLM大模型让程序员们开始重新评估自己的本领。 “AI会取代那些行业
?”“谁的饭碗又将不保了?
”等问题热议不断。
事实上,抢你饭碗的不是AI,而是会利用AI的人。
继科大讯飞、阿里、华为
等巨头公司发布AI产品后,很多中小企业也陆续进场!超高年薪,挖掘AI大模型人才! 如今大厂老板们,也更倾向于会AI的人,普通程序员,还有应对的机会吗?
与其焦虑……
不如成为「掌握AI工具的技术人
」,毕竟AI时代,谁先尝试,谁就能占得先机!
但是LLM相关的内容很多,现在网上的老课程老教材关于LLM又太少。所以现在小白入门就只能靠自学,学习成本和门槛很高。
基于此,我用做产品的心态来打磨这份大模型教程,深挖痛点并持续修改了近70次后,终于把整个AI大模型的学习门槛,降到了最低!
在这个版本当中:
第一您不需要具备任何算法和数学的基础
第二不要求准备高配置的电脑
第三不必懂Python等任何编程语言
您只需要听我讲,跟着我做即可,为了让学习的道路变得更简单,这份大模型教程已经给大家整理并打包,现在将这份 LLM大模型资料
分享出来:包括LLM大模型书籍、640套大模型行业报告、LLM大模型学习视频、LLM大模型学习路线、开源大模型学习教程
等, 😝有需要的小伙伴,可以 扫描下方二维码领取🆓↓↓↓
一、LLM大模型经典书籍
AI大模型已经成为了当今科技领域的一大热点,那以下这些大模型书籍就是非常不错的学习资源。
二、640套LLM大模型报告合集
这套包含640份报告的合集,涵盖了大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。(几乎涵盖所有行业)
三、LLM大模型系列视频教程
四、LLM大模型开源教程(LLaLA/Meta/chatglm/chatgpt)
五、AI产品经理大模型教程
LLM大模型学习路线 ↓
阶段1:AI大模型时代的基础理解
-
目标:了解AI大模型的基本概念、发展历程和核心原理。
-
内容:
- L1.1 人工智能简述与大模型起源
- L1.2 大模型与通用人工智能
- L1.3 GPT模型的发展历程
- L1.4 模型工程
- L1.4.1 知识大模型
- L1.4.2 生产大模型
- L1.4.3 模型工程方法论
- L1.4.4 模型工程实践
- L1.5 GPT应用案例
阶段2:AI大模型API应用开发工程
-
目标:掌握AI大模型API的使用和开发,以及相关的编程技能。
-
内容:
- L2.1 API接口
- L2.1.1 OpenAI API接口
- L2.1.2 Python接口接入
- L2.1.3 BOT工具类框架
- L2.1.4 代码示例
- L2.2 Prompt框架
- L2.3 流水线工程
- L2.4 总结与展望
阶段3:AI大模型应用架构实践
-
目标:深入理解AI大模型的应用架构,并能够进行私有化部署。
-
内容:
- L3.1 Agent模型框架
- L3.2 MetaGPT
- L3.3 ChatGLM
- L3.4 LLAMA
- L3.5 其他大模型介绍
阶段4:AI大模型私有化部署
-
目标:掌握多种AI大模型的私有化部署,包括多模态和特定领域模型。
-
内容:
- L4.1 模型私有化部署概述
- L4.2 模型私有化部署的关键技术
- L4.3 模型私有化部署的实施步骤
- L4.4 模型私有化部署的应用场景
这份 LLM大模型资料
包括LLM大模型书籍、640套大模型行业报告、LLM大模型学习视频、LLM大模型学习路线、开源大模型学习教程
等, 😝有需要的小伙伴,可以 扫描下方二维码领取🆓↓↓↓
更多推荐
所有评论(0)