这其实是研究MCP的前奏,MCP在我目前认知是为了统一模型的function call。
而对于function call,目前个别博客,媒体等纯属学院派,东拉西扯宏大蓝图,一幅誓叫日月换新天的架势,对于优化适配及异常处理等枯燥却实际的问题缄口不言,最后都是流量,都是卖课。
但现阶段通过自然语言调度函数工具构建业务流水线的意图实则拆东补西,美好的愿景下是底层程序员日以继夜的缝缝补补,
1 代码实现
1.1 tools的定义(格式这么写也是deepseek要求的,不知道其他模型的要求是不是不一样)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "这个方法用于获取天气, 当用户询问某个地点的天气时立即调用此函数",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "一个城市的经纬度字符串或者是adcode",
}
},
"required": ["location"]
},
}
},
{
"type": "function",
"function": {
"name": "add",
"description": "计算2个数字相加的和, 当有人问你2个数字的和的时候调用此方法",
"parameters": {
"type": "object",
"properties": {
"a": {"type": "number", "description": "第一个数字"},
"b": {"type": "number", "description": "第二个数字"}
},
"required": ["a", "b"]
},
}
},
]
# 用的国内的一个天气服务,需要注册,一定量内是免费的
def get_weather(location):
url = "https://XXXX.qweatherapi.com/v7/weather/now"
params = {
"location": location
}
headers = {
"X-QW-Api-Key": "<你自己的API-KEY>",
"Accept-Encoding": "gzip" # 对应 --compressed
}
try:
response = requests.get(url, params=params, headers=headers)
response.raise_for_status() # 检查请求是否成功
return response.json()
except Exception as e:
print(f"其他错误: {e}")
# weather_res = get_weather("101010100")
# print(weather_res)
# print(f"{weather_res['now']['temp']} 摄氏度")
# 就是想做个离谱的方法
def add(a, b):
return 1111
1.2 调用
def send_messages(messages):
response = client.chat.completions.create(
model="deepseek-chat", # 这个支持function call
messages=messages,
tools=tools,
tool_choice="auto"
)
return response.choices[0].message
client = OpenAI(
api_key="你自己的api-key",
base_url="https://api.deepseek.com",
)
# messages = [{"role": "user", "content": "30.6921,104.0435的天气怎么样"}] 这个理解有点点问题
# messages = [{"role": "user", "content": "成都的天气怎么样, 它的经纬度是30.6921,104.0435"}]
messages = [{"role": "user", "content": "10和20相加等于几"}]
resp_1 = send_messages(messages)
print(f"resp_1 = {resp_1}")
tool = resp_1.tool_calls[0]
messages.append(resp_1) # 这个还必须加上,注掉还不行
# 朴实无华的if else
if hasattr(resp_1, 'tool_calls') and resp_1.tool_calls:
function_name = resp_1.tool_calls[0].function.name
# 不能保证每个模型都返回这种结构,要适配不同模型,或者用提示词强行修改,不过效果不好罢了
arguments = json.loads(resp_1.tool_calls[0].function.arguments)
# 真正执行
if function_name == "get_weather":
real_result = get_weather(arguments['location'])
elif function_name == "add":
real_result = add(arguments['a'], arguments['b'])
else:
raise Exception("no function support")
tool_call_id = resp_1.tool_calls[0].id #这个必须对上
messages.append({"role": "tool", "tool_call_id": tool_call_id, "content": str(real_result)}) # 这里的toolid也要适配, 不同模型
# 把真实结果返回给DeepSeek
second_response = send_messages(messages)
print(f"second_response = {second_response}") # 结果还给我改了咋的?
2 反馈
- 模型必须选好,我选的是deepseek的deepseek-chat,它支持function call而且也便宜,这些模型是专门训练来输出function call的JSON返回。其他没有微调的模型估计得写很多提示词才可以
- 写问题的时候,也会有模型理解不到的情况,比如
[{"role": "user", "content": "30.6921,104.0435的天气怎么样"}],最后替换成[{"role": "user", "content": "成都的天气怎么样, 它的经纬度是30.6921,104.0435"}] - function call还必须把最先提的问题,返回的function call格式的JSON,以及本地调用的结果,全部传回大模型,最终才能返回有效结果,缺一都要报错。
- messages的最后一次拼接还得加上tool_call_id,这个是之前大模型饭给你的,你还得带回去,我感觉不同模型的格式可能会不一样,适配得花大力气
- 我写了个假的add方法,它恒返回1111,以function call的形式调用大模型,并将1111作为结果返回隔日大模型,它最后给我私自改对成30了,这个也是麻烦事情,大模型可能会篡改。
- 最后期望后面学习MCP的时候,上述情况能有所改观
1402

被折叠的 条评论
为什么被折叠?



