当前位置: 首页 > backend >正文

多轮Function Calling的最佳实践

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].messageexcept 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}&region={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
}
http://www.xdnf.cn/news/1289.html

相关文章:

  • 解决找不到字体的问题
  • org.springframework.beans.factory.config.YamlPropertiesFactoryBean 类详解
  • Java函数生成实际应用案例:数据处理流水线
  • 代理设计模式:从底层原理到源代码 详解
  • RT-Thread学习笔记(三)
  • 从零开始学java--二叉树和哈希表
  • 工作中sql总结
  • 无需复杂操作即可锁定键鼠的工具
  • [大模型]什么是function calling?
  • Linux操作系统--进程程序替换and做一个简单的shell
  • 3.6/Q1,Charls数据库经典文章解读
  • 【第九章 Python学习之函数Ⅱ】
  • 监控页面卡顿PerformanceObserver
  • idea快捷键 Project tool window
  • MySQL 性能监控工具的多维度对比分析
  • 出现了锁等待或死锁现象怎么办?乐观锁?分布式锁了解一下?
  • C语言教程(十三):C 语言中 enum(枚举)的详细介绍
  • C++ 学习指南
  • 一款强大的实时协作Markdown工具 | CodiMD 9.6K ⭐
  • 携程-酒旅-数据研发面经【附答案】
  • 【Spring】单例作用域下多次访问同一个接口
  • Discuz!+DeepSeek:传统论坛的智能化蜕变之路
  • 【C++】新手入门指南(下)
  • 《Linux TCP通信深度解析:实现高可靠工业数据传输客户端》
  • 使用Python设置excel单元格的字体(font值)
  • 笔记本电脑研发笔记:BIOS,Driver,Preloader详记
  • Win10一体机(MES电脑设置上电自动开机)
  • 《Android系统应用部署暗礁:OAT文件缺失引发的连锁崩溃与防御体系构建》
  • Mediatek Android13 设置Launcher
  • 基于ssm的疫情防控志愿者管理系统(源码+文档)