多轮Function Calling的最佳实践
每次函数执行完后,要把结果包装成 tool role 的 message,加到 messages 中,再次发送给大模型。每一轮调用的 messages 都要保留历史,确保大模型知道之前发生了什么,才能根据函数调用结果做出新的判断。使用 response.tool_calls 判断是否返回了函数调用请求,如果有就执行对应函数并追加结果。大模型可能连续多轮调用函数(比如先找坐标再找周边),所以需要用
·
1️⃣ 保持对话上下文完整
每一轮调用的 messages 都要保留历史,确保大模型知道之前发生了什么,才能根据函数调用结果做出新的判断。
2️⃣ 判断是否需要工具调用
使用 response.tool_calls 判断是否返回了函数调用请求,如果有就执行对应函数并追加结果。
3️⃣ 使用 while 循环处理多轮
大模型可能连续多轮调用函数(比如先找坐标再找周边),所以需要用 while 循环持续判断是否还有函数调用。
4️⃣ 将工具调用结果加入 messages
每次函数执行完后,要把结果包装成 tool role 的 message,加到 messages 中,再次发送给大模型。
5️⃣ 加入容错机制
加上 try…except 来处理 API 错误或参数问题,避免中断程序。
示例程序:
import os
import json
import requests
from openai import OpenAI
from dotenv import load_dotenv, find_dotenv
# 加载环境变量
load_dotenv(find_dotenv())
# 读取 Key 和基础 URL
amap_key = os.getenv("AMAP_KEY")
amap_base_url = os.getenv("AMAP_URL", "https://restapi.amap.com/v5")
# 初始化 OpenAI 客户端
client = OpenAI()
# JSON 打印工具
def print_json(data):
if hasattr(data, 'model_dump'):
data = data.model_dump()
elif hasattr(data, '__dict__'):
data = data.__dict__
print(json.dumps(data, indent=2, ensure_ascii=False))
# 生成大模型回复
def get_completion(messages, model="gpt-4o"):
try:
response = client.chat.completions.create(
model=model,
messages=messages,
temperature=0,
seed=1024,
tool_choice="auto",
tools=[
{
"type": "function",
"function": {
"name": "get_location_coordinate",
"description": "根据POI名称,获得POI的经纬度坐标",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "POI名称,必须是中文"},
"city": {"type": "string", "description": "POI所在城市,必须是中文"}
},
"required": ["location", "city"]
}
}
},
{
"type": "function",
"function": {
"name": "search_nearby_pois",
"description": "搜索给定坐标附近的POI",
"parameters": {
"type": "object",
"properties": {
"longitude": {"type": "number", "description": "经度"},
"latitude": {"type": "number", "description": "纬度"},
"keyword": {"type": "string", "description": "关键词,如咖啡、酒店等"}
},
"required": ["longitude", "latitude", "keyword"]
}
}
}
]
)
return response.choices[0].message
except Exception as e:
print(f"调用大模型失败:{str(e)}")
exit()
# 实现获取坐标功能
def get_location_coordinate(location, city):
url = f"{amap_base_url}/place/text?key={amap_key}&keywords={location}®ion={city}"
r = requests.get(url)
result = r.json()
if "pois" in result and result["pois"]:
poi = result["pois"][0]
lng, lat = poi.get("location").split(",")
return {
"name": poi.get("name"),
"longitude": float(lng),
"latitude": float(lat)
}
return None
# 实现搜索附近 POI 功能
def search_nearby_pois(longitude, latitude, keyword, debug=False):
url = f"{amap_base_url}/place/around"
params = {
"key": amap_key,
"keywords": keyword,
"location": f"{longitude},{latitude}",
"radius": 3000,
"s": "rsv3"
}
try:
r = requests.get(url, params=params)
if debug:
print("请求的 URL:", r.url)
print("HTTP 状态码:", r.status_code)
print("原始响应内容:", r.text)
if r.status_code != 200:
return f"请求失败,状态码:{r.status_code}"
result = r.json()
ans = ""
if "pois" in result and result["pois"]:
for i in range(min(3, len(result["pois"]))):
poi = result["pois"][i]
ans += f"{poi['name']}\n{poi['address']}\n距离:{poi.get('distance', '?')}米\n\n"
return ans.strip()
else:
return "附近没有搜索到相关的店铺,请尝试更换地点或关键词。"
except Exception as e:
return f"解析高德 API 响应时出错:{str(e)}"
# 用户输入
prompt = "我想在五道口附近喝咖啡,给我推荐几个"
messages = [
{"role": "system", "content": "你是一个地图通,你可以找到任何地址。"},
{"role": "user", "content": prompt}
]
# 初次调用大模型
response = get_completion(messages)
messages.append(response)
print("===== GPT 首轮回复 =====")
print_json(response)
# 多轮函数调用
while getattr(response, "tool_calls", None):
for tool_call in response.tool_calls:
args = json.loads(tool_call.function.arguments)
print("=== 调用函数参数 ===")
print_json(args)
# 执行对应函数
if tool_call.function.name == "get_location_coordinate":
result = get_location_coordinate(**args)
elif tool_call.function.name == "search_nearby_pois":
result = search_nearby_pois(**args, debug=True)
else:
result = "未知函数"
print("=== 函数返回结果 ===")
print_json(result)
# 提交工具调用结果
messages.append({
"tool_call_id": tool_call.id,
"role": "tool",
"name": tool_call.function.name,
"content": json.dumps(result, ensure_ascii=False)
})
# 再次获取大模型回复
response = get_completion(messages)
messages.append(response)
print("===== 最终大模型回复 =====")
print(response.content or "⚠️ 大模型未生成有效回复。")
GPT 的回复:
=====GPT回复=====
{
"content": null,
"refusal": null,
"role": "assistant",
"function_call": null,
"tool_calls": [
{
"id": "call_BEjN2hy7nriCqmWFGGvoyNmt",
"function": {
"arguments": "{\"location\":\"五道口\",\"city\":\"北京\"}",
"name": "get_location_coordinate"
},
"type": "function"
}
]
}
函数参数展开:
{
"location": "五道口",
"city": "北京"
}
Call: get_location_coordinate
=====函数返回=====
{
"parent": "",
"address": "海淀区",
"distance": "",
"pcode": "110000",
"adcode": "110108",
"pname": "北京市",
"cityname": "北京市",
"type": "地名地址信息;热点地名;热点地名",
"typecode": "190700",
"adname": "海淀区",
"citycode": "010",
"name": "五道口",
"location": "116.338611,39.992552",
"id": "B000A8WSBH"
}
函数参数展开:
{
"longitude": "116.338611",
"latitude": "39.992552",
"keyword": "咖啡"
}
Call: search_nearby_pois
=====函数返回=====
PAGEONE CAFE(五道口购物中心店)
成府路28号五道口购物中心(五道口地铁站B南口步行190米)
距离:9米
星巴克(北京五道口购物中心店)
成府路28号1层101-10B及2层201-09号
距离:39米
luckin coffee 瑞幸咖啡(五道口购物中心店)
成府路28号五道口购物中心负一层101号
距离:67米
=====最终回复=====
在五道口附近有以下几家咖啡店推荐:
1. **PAGEONE CAFE(五道口购物中心店)**
- 地址:成府路28号五道口购物中心(五道口地铁站B南口步行190米)
- 距离:9米
2. **星巴克(北京五道口购物中心店)**
- 地址:成府路28号1层101-10B及2层201-09号
- 距离:39米
3. **luckin coffee 瑞幸咖啡(五道口购物中心店)**
- 地址:成府路28号五道口购物中心负一层101号
- 距离:67米
希望你能找到一个满意的地方享受咖啡时光!
=====对话历史=====
{
"role": "system",
"content": "你是一个地图通,你可以找到任何地址。"
}
{
"role": "user",
"content": "我想在五道口附近喝咖啡,给我推荐几个"
}
{
"content": null,
"refusal": null,
"role": "assistant",
"function_call": null,
"tool_calls": [
{
"id": "call_BEjN2hy7nriCqmWFGGvoyNmt",
"function": {
"arguments": "{\"location\":\"五道口\",\"city\":\"北京\"}",
"name": "get_location_coordinate"
},
"type": "function"
}
]
}
{
"tool_call_id": "call_BEjN2hy7nriCqmWFGGvoyNmt",
"role": "tool",
"name": "get_location_coordinate",
"content": "{'parent': '', 'address': '海淀区', 'distance': '', 'pcode': '110000', 'adcode': '110108', 'pname': '北京市', 'cityname': '北京市', 'type': '地名地址信息;热点地名;热点地名', 'typecode': '190700', 'adname': '海淀区', 'citycode': '010', 'name': '五道口', 'location': '116.338611,39.992552', 'id': 'B000A8WSBH'}"
}
{
"content": null,
"refusal": null,
"role": "assistant",
"function_call": null,
"tool_calls": [
{
"id": "call_PuOC0rCTct8cSbHoT3rAHTee",
"function": {
"arguments": "{\"longitude\":\"116.338611\",\"latitude\":\"39.992552\",\"keyword\":\"咖啡\"}",
"name": "search_nearby_pois"
},
"type": "function"
}
]
}
{
"tool_call_id": "call_PuOC0rCTct8cSbHoT3rAHTee",
"role": "tool",
"name": "search_nearby_pois",
"content": "PAGEONE CAFE(五道口购物中心店)\n成府路28号五道口购物中心(五道口地铁站B南口步行190米)\n距离:9米\n\n星巴克(北京五道口购物中心店)\n成府路28号1层101-10B及2层201-09号\n距离:39米\n\nluckin coffee 瑞幸咖啡(五道口购物中心店)\n成府路28号五道口购物中心负一层101号\n距离:67米\n\n"
}
{
"content": "在五道口附近有以下几家咖啡店推荐:\n\n1. **PAGEONE CAFE(五道口购物中心店)**\n - 地址:成府路28号五道口购物中心(五道口地铁站B南口步行190米)\n - 距离:9米\n\n2. **星巴克(北京五道口购物中心店)**\n - 地址:成府路28号1层101-10B及2层201-09号\n - 距离:39米\n\n3. **luckin coffee 瑞幸咖啡(五道口购物中心店)**\n - 地址:成府路28号五道口购物中心负一层101号\n - 距离:67米\n\n希望你能找到一个满意的地方享受咖啡时光!",
"refusal": null,
"role": "assistant",
"function_call": null,
"tool_calls": null
}
更多推荐



所有评论(0)