06 Function Calling&MCP
摘要
本资料深入探讨了Function Calling和Model Context Protocol (MCP) 两大技术。Function Calling是LLM与外部系统交互的“桥梁”,扩展模型能力,而MCP作为开放协议标准,旨在统一LLM与多数据源、工具及服务之间的交互,被誉为“AI领域的USB-C接口”。笔记将详细阐述它们的概念、区别、应用场景及代码实践。
Function Calling
核心概念
Function Calling是大型语言模型(LLM)与真实世界交互的重要机制,它允许LLM通过调用预设的函数来执行模型本身无法完成的任务。
作用:
- 扩展模型能力: LLM可以获取实时数据(天气、股价)、执行复杂计算(数学运算、代码执行)或操作外部系统(发送邮件、控制智能设备)。
- 结构化输出: 将用户的自然语言请求转化为结构化参数,传递给函数。例如,将“明天北京天气如何?”转化为
get_weather(location="北京", date="2025-05-06")。 - 动态决策流程: 模型能根据上下文决定何时调用函数,甚至进行链式调用。
Function Calling与MCP的区别
维度 | Function Calling | MCP (Model Context Protocol)
定位 | 模型厂商私有接口(如OpenAI, Qwen) | 开放协议(类似HTTP/USB-C),Anthropic于2024年11月推出
扩展性 | 需为每个模型单独适配 | 一次开发,多模型兼容
复杂性 | 适合简单、单次调用任务 | 支持多轮对话、复杂上下文管理
生态依赖 | 依赖特定模型(如GPT-4) | 跨模型、跨平台(如Claude、Cursor)
安全性 | 依赖云端API 密钥 | 支持本地化数据控制
Function Calling的必要性
尽管MCP可能成为主流,Function Calling作为底层能力仍将存在。对于简单、原子化的任务,Function Calling开发快捷、延迟低,无需MCP Server配置,直接通过模型API调用预定义函数更为方便。
案例:门票助手
此案例展示了如何使用Qwen-Agent结合Function Calling搭建一个门票业务查询助手。
整体搭建流程:
- 系统初始化: 设置系统Prompt,描述门票表结构和查询需求,注册SQL查询工具(
exc_sql)。 - 助手实例化: 使用Qwen-Agent的
Assistant类,加载LLM配置、系统Prompt和function_list。 - 设置交互模式: 选择WebUI模式,用户输入问题,助手自动完成SQL查询并返回结果。
- Function Call机制: LLM解析意图生成SQL,
exc_sql工具执行SQL并返回结果。
system_prompt 示例:
system_prompt = """我是门票助手,以下是关于门票订单表相关的字段,我可能会编写对应的SQL,对数据进行查询
-- 门票订单表
CREATE TABLE tkt_orders (
order_time DATETIME, -- 订单日期
account_id INT, -- 预定用户ID
gov_id VARCHAR(18), -- 商品使用人ID(身份证号)
gender VARCHAR(10), -- 使用人性别
age INT, -- 年龄
province VARCHAR(30), -- 使用人省份
SKU VARCHAR(100), -- 商品SKU名
product_serial_no VARCHAR(30), -- 商品ID
eco_main_order_id VARCHAR(20), -- 订单ID
sales_channel VARCHAR(20), -- 销售渠道
status VARCHAR(30), -- 商品状态
-- ... 省略其他字段
)
"""
exc_sql 工具注册示例:
from qwen_agent.tools.base import BaseTool, register_tool
import pandas as pd
import json
# 导入数据库连接(此处省略具体实现)
# from sqlalchemy import create_engine
# engine = create_engine('mysql+mysqlconnector://user:password@host/database')
@register_tool('exc_sql')
class ExcSQLTool(BaseTool):
"""
SQL查询工具,执行传入的SQL语句并返回结果。
"""
description = '对于生成的SQL,进行SQL查询'
parameters = [{
'name': 'sql_input',
'type': 'string',
'description': '生成的SQL语句',
'required': True
}]
def call(self, params: str, **kwargs) -> str:
args = json.loads(params)
sql_input = args['sql_input']
database = args.get('database', 'ubr') # 假设数据库名为ubr
# 创建数据库连接 (实际使用时需要具体实现)
engine = None # Placeholder for actual database engine
try:
df = pd.read_sql(sql_input, engine)
# 返回前10行,防止数据过多
return df.head(10).to_markdown(index=False)
except Exception as e:
return f"SQL执行出错: {str(e)}"
Assistant 初始化与 function_list:
from qwen_agent.agents import Assistant
from qwen_agent.gui import WebUI
def init_agent_service():
"""初始化门票助手服务"""
llm_cfg = {
'model': 'qwen-turbo-2025-04-28',
'timeout': 30,
'retry_count': 3,
}
try:
bot = Assistant(
llm=llm_cfg,
name='门票助手',
description='门票查询与订单分析',
system_message=system_prompt,
function_list=['exc_sql'], # 只传工具名字符串
)
print("助手初始化成功!")
return bot
except Exception as e:
print(f"助手初始化失败: {str(e)}")
raise
# WebUI 启动示例
# if __name__ == '__main__':
# bot = init_agent_service()
# chatbot_config = {
# 'prompt.suggestions': [
# '2023年4、5、6月一日门票,二日门票的销量多少?帮我按照周进行统计',
# '2023年7月的不同省份的入园人数统计',
# '帮我查看2023年10月1-7日销售渠道订单金额排名',
# ]
# }
# WebUI(bot, chatbot_config=chatbot_config).run()
可视化图表呈现
为了在SQL查询结果后自动生成可视化图表,可以将数据查询和可视化集成到同一个工具中。
实现步骤(集成到exc_sql工具中):
- SQL查询获取数据: 执行SQL,获取DataFrame,生成Markdown表格。
- 自动推断图表字段: x轴优先选择第一个字符串类型列,y轴选择所有数值类型列。
- 柱状图绘制: 使用
matplotlib绘制柱状图,支持多系列数据并自动调整柱宽和位置。 - 图表样式设置: 设置标签、标题、旋转x轴标签、添加图例等。
- 图表保存与返回: 保存图表为PNG文件,生成Markdown格式的图片引用,返回“表格+图片”组合结果。
示例代码片段(集成到call方法中,在df获取后):
# ... (inside ExcSQLTool.call method after df is obtained) ...
import matplotlib.pyplot as plt
import os
import time
# ... (Step 1: SQL查询及markdown生成已完成) ...
# Step 2: 自动推断图表字段
x_candidates = df.select_dtypes(include=['object']).columns.tolist()
x = x_candidates[0] if x_candidates else (df.columns.tolist()[0] if not df.empty else None)
y_candidates = df.select_dtypes(include=['number']).columns.tolist()
y_fields = y_candidates if y_candidates else (df.columns.tolist()[1:] if not df.empty and len(df.columns) > 1 else [])
if x and y_fields and not df.empty:
plt.figure(figsize=(8, 5))
bar_width = 0.35 if len(y_fields) > 1 else 0.6
x_labels = df[x].astype(str)
x_pos = range(len(df))
# Step 3: 柱状图绘制
for idx, y_col in enumerate(y_fields):
plt.bar([p + idx * bar_width for p in x_pos], df[y_col], width=bar_width, label=y_col)
# Step 4: 图表样式设置
plt.xlabel(x)
plt.ylabel(','.join(y_fields))
plt.title(f"{' & '.join(y_fields)} by {x}")
plt.xticks([p + bar_width * (len(y_fields) - 1) / 2 for p in x_pos], x_labels, rotation=45, ha='right')
plt.legend()
plt.tight_layout()
# Step 5: 图表保存与返回
save_dir = os.path.join(os.path.dirname(__file__), 'image_show')
os.makedirs(save_dir, exist_ok=True)
filename = f'bar_{int(time.time()*1000)}.png'
save_path = os.path.join(save_dir, filename)
plt.savefig(save_path)
plt.close()
img_path = os.path.join('image_show', filename)
img_md = f'!柱状图({img_path})'
return f"{md}\n\n{img_md}"
else:
return md # 如果无法绘图,只返回markdown
版本更新:
- assistant_ticket_bot-1: 实现Function Calling调用
exc_sql。 - assistant_ticket_bot-2: 添加基本的图表绘制功能。
- assistant_ticket_bot-3: 完善图表功能,支持多类别变量的透视图可视化(通过
pd.pivot_table)。
实践练习
利用LLM/Agent + Function Calling,基于本地MySQL数据进行业务查询(例如:月销量、环比增长、省份销售额、Top渠道等)。
MCP使用
什么是MCP
Model Context Protocol (MCP) 是Anthropic公司于2024年11月推出的开放协议标准,旨在标准化LLM与外部数据源、工具及服务之间的交互方式。它被广泛类比为“AI领域的USB-C接口”,目标是实现统一接口和互操作性。
MCP的核心概念
-
架构与组件: 采用客户端-服务器(Client-Server)架构。
Code_MCP Host:_* 运行AI模型的环境(如Claude Desktop, Cursor IDE)。 _MCP Client:_* 嵌入在Host中,负责发起请求并与MCP Server通信。 _MCP Server:_* 轻量级服务,提供特定功能(数据查询、API调用)。 -
核心功能:
Code_Resources(知识扩展):_* 提供结构化数据以增强AI上下文理解。 _Tools(工具调用):_* 允许AI执行外部操作(如发送邮件、查询GitHub)。 _Prompts(提示模板):_* 预定义的指令模板,优化AI任务执行。
案例:旅游攻略MCP (高德地图)
此案例演示如何将高德地图MCP服务集成到支持MCP的客户端中,生成旅游攻略。
步骤:
- 获取高德地图MCP使用授权 (lbs.amap.com)。
- 注册开发者,创建应用并获取API KEY。
- 配置MCP客户端(如Cursor, Cherry Studio, GitHub Copilot)的
mcp.json文件。
mcp.json 配置示例 (适用于Cursor/Cherry Studio/GitHub Copilot):
{
"mcpServers": {
"amap-maps": {
"command": "npx",
"args": [
"-y",
"@amap/amap-maps-mcp-server"
],
"env": {
"AMAP_MAPS_API_KEY": "你的key"
}
}
}
}
配置完成后,在客户端的Agent模式下提问即可,如“用高德MCP,做上海一天旅游攻略”。
Cherry Studio中的MCP配置:
- 确保UV, Bun工具安装。
- 搜索
@amap并添加MCP服务,保存并打开服务。 - 配置LLM(如
qwen-turbo),选择模型并打开MCP服务器,然后提问。
案例:Tavily 搜索MCP
此案例展示如何集成Tavily实时网络搜索MCP。
步骤:
- 添加Tavily MCP配置到客户端的
mcpServers中。
mcpServers 配置示例:
{
"mcpServers": {
"tavily-mcp": {
"args": [
"-y",
"tavily-mcp@0.1.4"
],
"autoApprove": [],
"command": "npx",
"disabled": false,
"env": {
"TAVILY_API_KEY": "your-api-key-here"
}
}
}
}
- 使用客户端调用,如“用Tavily MCP,查找黄金相关的新闻”。
Qwen-Agent + Tavily集成:
通过调整chatbot_config的prompt.suggestions,引导LLM使用Tavily工具进行搜索。
Tavily提供了tavily-search(实时网络搜索)和tavily-extract(网页内容智能提取)两个主要工具。
tavily-search参数:
query(必填,字符串):搜索关键词。max_results(可选,整数):返回结果条数。search_depth(可选,枚举basic/advanced):搜索深度。include_domains/exclude_domains(可选,数组):限定或排除域名。time_range(可选,字符串):时间过滤。include_answer(可选,布尔):是否要求直接返回答案。include_raw_content(可选,布尔):是否携带原始HTML。
tavily-extract参数:
urls(必填,字符串或数组):要提取的页面地址。extract_depth(可选,枚举basic/advanced):提取深度。format(可选,枚举text/markdown/json):返回格式。include_images(可选,布尔):是否同时提取图片链接。
案例:桌面TXT统计器(MCP SDK使用)
此案例演示如何使用Python MCP SDK(FastMCP)搭建一个本地桌面文件统计器。
MCP SDK功能:
pip install mcp- 创建MCP Server: 提供标准化API。
- 注册工具: 通过
@mcp.tool()装饰器将Python函数暴露给AI模型。 - 安全交互: 支持权限控制。
- 跨平台兼容: 与OpenAI、Anthropic Claude等LLM集成。
FastMCP功能:
- Python MCP SDK中的轻量级服务器框架。
- 简单易用,几行代码即可启动。
- 支持多种传输方式(stdio、HTTP)。
- 通过
@mcp.tool()自动工具发现。
桌面TXT统计器代码示例:
import os
from pathlib import Path
from mcp.server.fastmcp import FastMCP
# 创建MCP Server
mcp = FastMCP("桌面TXT 文件统计器")
@mcp.tool()
def count_desktop_txt_files() -> int:
"""统计桌面上.txt 文件的数量"""
# 获取桌面路径
desktop_path = Path(os.path.expanduser("~/Desktop"))
# 统计.txt 文件
txt_files = list(desktop_path.glob("*.txt"))
return len(txt_files)
@mcp.tool()
def list_desktop_txt_files() -> str:
"""获取桌面上所有.txt 文件的列表"""
# 获取桌面路径
desktop_path = Path(os.path.expanduser("~/Desktop"))
# 获取所有.txt 文件
txt_files = list(desktop_path.glob("*.txt"))
# 返回文件名列表
if not txt_files:
return "桌面上没有找到.txt 文件。"
# 格式化文件名列表
file_list = "\n".join([f"- {file.name}" for file in txt_files])
return f"在桌面上找到{len(txt_files)} 个.txt 文件:\n{file_list}"
if __name__ == "__main__":
# 初始化并运行服务器
mcp.run()
运行与测试:
-
在终端使用
mcp dev txt_counter.py命令启动服务器。 -
打开浏览器访问
http://localhost:5173/,即可使用MCP Inspector测试工具。-
点击Connect。
-
在Tools中选择
count_desktop_txt_files或list_desktop_txt_files。 -
点击Run Tool查看结果。
-
在History中可查看请求情况。
-
MCP Inspector
MCP Inspector是专为MCP Server设计的开源调试工具,用于测试和优化MCP Server功能。
- 服务器连接管理: 支持本地和远程MCP服务器连接,可配置传输方式(STDIO或HTTP+SSE),方便调试。
- 服务浏览与调用: 提供可视化界面(默认
http://localhost:5173),展示MCP服务器提供的工具、资源、提示等服务。可以直接在UI中调用工具并查看执行记录。
UV工具
在MCP协议中,uv工具常用于启动和管理MCP服务器,例如通过uv命令指定运行的Python脚本来启动MCP服务器。
实践练习
使用Qwen-Agent或其他具有Agent能力的客户端(如Cursor, Cherry Studio),集成MCP服务并通过自然语言调用(例如:规划自驾游、获取网页内容、检索新闻、统计桌面文件数量)。
关键要点
- Function Calling 扩展LLM与外部系统的交互能力,实现结构化输出和动态决策,适用于简单原子化任务。
- MCP (Model Context Protocol) 是一个开放协议标准,旨在统一LLM与多数据源、工具和服务的交互,提升跨模型兼容性。
- Function Calling和MCP各有侧重,Function Calling作为底层能力仍不可或缺,而MCP更关注跨平台和复杂场景的标准化。
- MCP SDK和FastMCP 简化了自定义MCP Server的开发和工具注册,使得本地功能也能被LLM调用。
- MCP Inspector 是调试和测试MCP Server的强大可视化工具。