在这篇 MCP - AI智能体调用 MCP Serverr - Streamable HTTP(七) 文章中,虽然可以实现 MCP tools 的调用,但测试下来效果不好。
例如:有如下tools代码,其中提供 3个tools
- search_flights_between_countries: 查询两个国家之间的国际航班信息及票价
- search_international_multi_city: 国际机票查询服务
- search_domestic_multi_city: 中国(国内)机票查询服务
# -*- coding: utf-8 -*-
"""
航班查询
"""
from base.tool_registry import mcp_tool@mcp_tool()
def search_flights_between_countries(departure_country: str="中国", arrival_country: str="美国", departure_date: str="2026/02/10",max_duration: int=0,max_total_price: int=0,sort_by: list[str]=[],limit: int=0
):"""查询两个国家之间的国际航班信息及票价提供指定日期内从出发国家到目的国家的航班搜索服务,返回包含航班详情、价格、时间等信息的结构化数据。支持全球主要国家和城市的航班查询。Args:departure_country (str): 出发国家名称arrival_country (str): 到达国家名称departure_date (str): 出发日期,格式为 YYYY-MM-DDmax_duration (int): 最大飞行时长,单位:小时,默认值为 24小时max_total_price (int): 机票最大总价格,单位:元,默认值为 5000元sort_by (list[str]): 排序规则字段,默认值为 ['total_price', 'duration'] 默认先按总价格排序,再按持续时间排序limit (int): 返回最优的记录条数,默认为20条Returns:str: 机票查询结果"""msg = f"查询两个国家之间的国际航班信息及票价: {departure_country} -> {arrival_country}, 日期: {departure_date}, 时长小于 {max_duration}小时, 总价小于 {max_total_price}元, 排序规则: {sort_by}, 返回: {limit}条"print(f"{msg}")return f"{msg}"@mcp_tool()
def search_international_multi_city(departure_list: list[str]=["北京"],arrival_list: list[str]=["上海"],departure_date: str="2026/02/10",max_duration: int=0,max_total_price: int=0,sort_by: list[str]=[],limit: int=0
):"""国际机票查询服务提供国内多个出发地到多个目的地的组合航班查询,支持单程多城市航线搜索。适用于复杂的国内旅行规划,如多城市环线旅行或不同出发地的团体订票。Args:departure_list (list[str]): 出发城市列表,例如:["北京", "上海"]arrival_list (list[str]): 目的城市列表,例如:["广州", "深圳"]departure_date (str): 出发日期,格式为 YYYY-MM-DDmax_duration (int): 最大飞行时长,单位:小时,默认值为 24小时max_total_price (int): 机票最大总价格,单位:元,默认值为 5000元sort_by (list[str]): 排序规则字段,默认值为 ['total_price', 'duration'] 默认先按总价格排序,再按持续时间排序limit (int): 返回最优的记录条数,默认为20条Returns:str: 机票查询结果"""msg = f"国际机票查询服务: {departure_list} -> {arrival_list}, 日期: {departure_date}, 时长小于 {max_duration}小时, 总价小于 {max_total_price}元, 排序规则: {sort_by}, 返回: {limit}条"print(f"{msg}")return f"{msg}"@mcp_tool()
def search_domestic_multi_city(departure_list: list[str]=["北京"],arrival_list: list[str]=["上海"],departure_date: str="2026/02/10",max_duration: int=0,max_total_price: int=0,sort_by: list[str]=[],limit: int=0
):"""中国(国内)机票查询服务提供国内多个出发地到多个目的地的组合航班查询,支持单程多城市航线搜索。适用于复杂的国内旅行规划,如多城市环线旅行或不同出发地的团体订票。Args:departure_list (list[str]): 出发城市列表,例如:["北京", "上海"]arrival_list (list[str]): 目的城市列表,例如:["广州", "深圳"]departure_date (str): 出发日期,格式为 YYYY-MM-DDmax_duration (int): 最大飞行时长,单位:小时,默认值为 24小时max_total_price (int): 机票最大总价格,单位:元,默认值为 5000元sort_by (list[str]): 排序规则字段,默认值为 ['total_price', 'duration'] 默认先按总价格排序,再按持续时间排序limit (int): 返回最优的记录条数,默认为20条Returns:str: 机票查询结果"""msg = f"中国(国内)机票查询服务: {departure_list} -> {arrival_list}, 日期: {departure_date}, 时长小于 {max_duration}小时, 总价小于 {max_total_price}元, 排序规则: {sort_by}, 返回: {limit}条"print(f"{msg}")return f"{msg}"
在咱们写的Agent中输入 帮我查找 中国 到 美国的 机票,时间 2025/10/01 ,可以看到大模型返回 tool_calls 是空,也就是不调用 MCP tools。
但在 Cherry Studio 中就能看到它可以调用 MCP tools,这是为什么呢?仔细查看后发现 Cherry Studio 使用的提示词跟咱们写的 Agent 不同。
问题分析:
一、MCP tools 调用流程
二、自己写的 Agent 智能体的请求
注意:使用抓包前需要修改 .env 配置文件,例如:
其中,url 根据你的情况修改,但注意需要添加 /v1 否则报 404 错误
API_KEY= BASE_URL=http://172.31.12.15:11434/v1 MODEL=qwen3:4b
使用抓包工具,抓包整理后得到图中右侧部分。可以看到发送了两个请求:
- 发送用户输入内容,和 tools 列表
- 发送 tools 调用结果 + 1的响应
2.1 第一个请求
2.2 第二个请求
三、cherry studio 的请求
3.1 处理流式传输
由于 cherry studio 配置的是流式传输,所以一个响应拆到了很多包的 content 字段里,抓包后需要手工合并。如下图:
3.2 第一个请求
3.3 第二个请求
3.4 将第一个请求 "role": "system" 中的 "content" 转为文本
可以看出 cherry 的跟自己实现的 agent 提示词不同。
In this environment you have access to a set of tools you can use to answer the user's question. You can use one or more tools per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.## Tool Use FormattingTool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:<tool_use><name>{tool_name}</name><arguments>{json_arguments}</arguments>
</tool_use>The tool name should be the exact name of the tool you are using, and the arguments should be a JSON object containing the parameters required by that tool. For example:
<tool_use><name>python_interpreter</name><arguments>{\"code\": \"5 + 3 + 1294.678\"}</arguments>
</tool_use>The user will respond with the result of the tool use, which should be formatted as follows:<tool_use_result><name>{tool_name}</name><result>{result}</result>
</tool_use_result>The result should be a string, which can represent a file or any other output type. You can use this result as input for the next action.
For example, if the result of the tool use is an image file, you can use it in the next action like this:<tool_use><name>image_transformer</name><arguments>{\"image\": \"image_1.jpg\"}</arguments>
</tool_use>Always adhere to this format for the tool use to ensure proper parsing and execution.## Tool Use ExamplesHere are a few examples using notional tools:
---
User: Generate an image of the oldest person in this document.Assistant: I can use the document_qa tool to find out who the oldest person is in the document.
<tool_use><name>document_qa</name><arguments>{\"document\": \"document.pdf\", \"question\": \"Who is the oldest person mentioned?\"}</arguments>
</tool_use>User: <tool_use_result><name>document_qa</name><result>John Doe, a 55 year old lumberjack living in Newfoundland.</result>
</tool_use_result>Assistant: I can use the image_generator tool to create a portrait of John Doe.
<tool_use><name>image_generator</name><arguments>{\"prompt\": \"A portrait of John Doe, a 55-year-old man living in Canada.\"}</arguments>
</tool_use>User: <tool_use_result><name>image_generator</name><result>image.png</result>
</tool_use_result>Assistant: the image is generated as image.png---
User: \"What is the result of the following operation: 5 + 3 + 1294.678?\"Assistant: I can use the python_interpreter tool to calculate the result of the operation.
<tool_use><name>python_interpreter</name><arguments>{\"code\": \"5 + 3 + 1294.678\"}</arguments>
</tool_use>User: <tool_use_result><name>python_interpreter</name><result>1302.678</result>
</tool_use_result>Assistant: The result of the operation is 1302.678.---
User: \"Which city has the highest population , Guangzhou or Shanghai?\"Assistant: I can use the search tool to find the population of Guangzhou.
<tool_use><name>search</name><arguments>{\"query\": \"Population Guangzhou\"}</arguments>
</tool_use>User: <tool_use_result><name>search</name><result>Guangzhou has a population of 15 million inhabitants as of 2021.</result>
</tool_use_result>Assistant: I can use the search tool to find the population of Shanghai.
<tool_use><name>search</name><arguments>{\"query\": \"Population Shanghai\"}</arguments>
</tool_use>User: <tool_use_result><name>search</name><result>26 million (2019)</result>
</tool_use_result>
Assistant: The population of Shanghai is 26 million, while Guangzhou has a population of 15 million. Therefore, Shanghai has the highest population.## Tool Use Available Tools
Above example were using notional tools that might not exist for you. You only have access to these tools:
<tools><tool><name>server-add_numbers</name><description>Add two numbers together\n\nArgs:\n a (int): parameter 1\n b (int): parameter 2\n\nReturns:\n The sum of individuals</description><arguments>\n {\"type\":\"object\",\"properties\":{\"a\":{\"title\":\"A\",\"type\":\"integer\"},\"b\":{\"title\":\"B\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"]}\n </arguments>
</tool></tools>## Tool Use Rules
Here are the rules you should always follow to solve your task:
1. Always use the right arguments for the tools. Never use variable names as the action arguments, use the value instead.
2. Call a tool only when needed: do not call the search agent if you do not need information, try to solve the task yourself.
3. If no tool call is needed, just answer the question directly.
4. Never re-do a tool call that you previously did with the exact same parameters.
5. For tool use, MARK SURE use XML tag format as shown in the examples above. Do not use any other format.# User InstructionsResponse in user query language.
Now Begin! If you solve the task correctly, you will receive a reward of $1,000,000.
但 cherry 的提示词也不能直接使用,需要再调整才能改善自己写的 agent 工具调用的效果:
- cherry 将 tools 列表放在了content;
- cherry 提示词中要求工具调用返回的结构不同,如果使用 cherry 提示词需要修改。
四、使用 cherry 提示词
4.1 代码中添加提示词
将以上 3.4 的提示词添加到代码中,然后将 tools 段替换为以下内容 {available_tools} ,添加后如下图:
4.2 添加 available_tools_xml 并修改 messages
代码如下:
# 获取工具列表 - XML 格式(用在 系统提示词 中)available_tools_xml = [f"\n <tool>\n <name>{tool.name}</name>\n <description>{tool.description}</description>\n <arguments>{tool.inputSchema}</arguments>\n </tool>\n"for tool in self.tools]# 待发给 AI 的消息messages = [{"role": "system", "content": system_prompt.replace("{available_tools}", "".join(available_tools_xml))}, {"role": "user", "content": query}]
diff 如下:
4.3 测试结果
修改前
帮我查询 中国 到 澳大利亚 的机票,时间 2026/2/10,并以markdown表格的形式显示出来 ✅
[Calling tool search_flights_between_countries {'departure_country': '中国', 'arrival_country': '澳大利亚', 'departure_date': '2026-02-10'}]帮我查询 北京 到 上海 的机票,时间 2025/10/10,并以markdown表格的形式显示出来 ❌ 没有找到工具,直接回复让我去自己搜索帮我查询 北京 到 纽约 的机票,时间 2025/10/10,并以markdown表格的形式显示出来 ❌ 没有找到工具,直接回复让我去自己搜索帮我查询 北京、上海 到 纽约、墨尔本 的机票,时间 2025/10/10,并以markdown表格的形式显示出来 ❌ 没有找到工具,直接回复让我去自己搜索帮我查询 北京、上海 到 南京 的机票,时间 2025/10/10,并以markdown表格的形式显示出来 ✅
[Calling tool search_domestic_multi_city with args {'departure_list': ['北京', '上海'], 'arrival_list': ['南京'], 'departure_date': '2025-10-10', 'limit': 20}]
修改 cherry 提示词后:
帮我查询 中国 到 澳大利亚 的机票,时间 2026/2/10,并以markdown表格的形式显示出来 ✅
[Calling tool search_flights_between_countries {'departure_country': '中国', 'arrival_country': '澳大利亚', 'departure_date': '2026-02-10'}]帮我查询 北京 到 上海 的机票,时间 2025/10/10,并以markdown表格的形式显示出来 ✅
[Calling tool search_domestic_multi_city {'departure_list': ['北京'], 'arrival_list': ['上海'], 'departure_date': '2025-10-10', 'limit': 20}]帮我查询 北京 到 纽约 的机票,时间 2025/10/10,并以markdown表格的形式显示出来 ❌ 应该调用国际城市查询,但调用国家查询
[Calling tool search_flights_between_countries with args {'departure_country': '中国', 'arrival_country': '美国', 'departure_date': '2025-10-10'}]帮我查询 北京、上海 到 纽约、墨尔本 的机票,时间 2025/10/10,并以markdown表格的形式显示出来 ✅
[Calling tool search_international_multi_city {'departure_list': ['北京', '上海'], 'arrival_list': ['纽约', '墨尔本'], 'departure_date': '2025-10-10', 'max_duration': 24, 'max_total_price': 5000, 'sort_by': ['total_price', 'duration'], 'limit': 20}]帮我查询 北京、上海 到 南京 的机票,时间 2025/10/10,并以markdown表格的形式显示出来 ✅
[Calling tool search_domestic_multi_city {'departure_list': ['北京', '上海'], 'arrival_list': ['南京'], 'departure_date': '2025-10-10', 'limit': 20}]
4.4 问题
以上的处理方法有一个问题,就是传了两份 tools 列表。可以如下图,注释请求中的tools。
但这样返回就会改成如下格式,需要再修改代码处理,这里就不展示了。
<tool_use>
<name>search_flights_between_countries</name>
<arguments>{"departure_country": "中国","arrival_country": "美国","departure_date": "2025-10-10"
}</arguments>
</tool_use>