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}&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
}
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐