ChatGPT 结合实际地图实现问答式地图检索功能基于Function calling

小明 2025-04-30 21:12:58 6

ChatGPT 结合实际地图实现问答式地图检索功能基于Function calling

ChatGPT结合实际业务,主要是研发多函数调用(Function Calling)功能模块,将自定义函数通过ChatGPT 问答结果,实现对应函数执行,再次将结果输入ChatGPT,大模型将结果规范化处理输出,完成一次完整的prompt,实现对话式应用场景实际功能实现。

Function calling是什么?

Function calling是可以让我们用自己的函数当作调用chatgpt的参数,在函数中我们可以做任何事情,例如获取网络上的数据,查询自己的数据库等。

为什么使用Function calling?

比如当我们问chatgpt:上海今天得天气怎么样?他得回答是:很抱歉,作为一个语言模型,我无法提供实时的天气信息…为什么这样呢,因为chatgpt没有实时地网络数据,而有了Function calling我们可以在函数中调用查询天气的接口返回给chatgpt,chatgpt按照我们的需求把数据转为自然语言。当然这只是一个最基础最简单的应用场景,实际会有更多更复杂的应用场景。

Function Calling 的机制

Function Calling 机制的主要关键点:

  1. OpenAI 通过用户输入Pormpt 和打包参数、函数描述等进行问答结构化输出;
  2. 匹配判断函数功能,(该部分可用大模型自带的Auto,但效果差),也可自行实现函数判断是否调用;
  3. 如果匹配成功,生成对函数调用的结构化参数;
  4. 调用自定义执行函数,获得函数执行结果,并将结果append 到起初Pormpt,再次送入大模型;
  5. 大模型对结果进行整合,结构化输出结果;
  6. 如果不匹配直接返回结果显示给用户;

    官方给出的具体流程

  7. 使用用户查询和函数参数中定义的一组外部函数库。
  8. 模型可以选择调用任意外部函数;如果是这样,内容将是符合自定义架构的字符串化 JSON 对象(注意:模型可能会生成无效的 JSON 或幻觉参数)。
  9. 在代码中将字符串解析为 JSON,并使用提供的参数调用函数(如果存在)。
  10. 通过将函数响应追加为新消息来再次调用模型,并让模型将结果汇总返回给用户。

Function Calling 的特点

Function Calling 是一种让 Chat Completion 模型调用外部函数的能力,可以让模型不仅仅根据自身的数据库知识进行回答,而是可以额外挂载一个函数库,然后根据用户提问去函数库检索,按照实际需求调用外部函数并获取函数运行结果,再基于函数运行结果进行回答。

支持 Function Calling 的非国产大模型

目前 OpenAI 仅支持 gpt-3.5-turbo-0613 和 gpt-4-0613 两个语言模型使用 Function Calling 功能。

支持 Function Calling 的国产大模型

  • 百度文心大模型 MiniMax:做虚拟人物效果不错。
  • ChatGLM3-6B:最著名的国产开源大模型,生态最好。
  • 讯飞星火 3.0。

    高德地图实际地图信息测试

    本次测试是利用对话方式实现地图目标搜索功能,具体任务如下图所示。

    用户提问: 帮我查找北京市三里屯附近的咖啡店

    GPT解答: 结构化输出:北京市、三里屯、咖啡店

    定义大模型结构化函数: 自定义大模型识别的自定义函数,实现函数功能;

    函数执行: 将GPT解答结果传参,传入自定义函数;

    结果回流: 将函数返回结果与直接Pormpt进行append,并一起传入大模型;

    GPT解答: 规范化结果输出。

    具体流程如下图所示

    外部函数编写规范

    在使用前需要我们先对外部函数进行定义和实现,实现就不多说就是一个具体函数。而定义需要我们包含以下几部分内容:

    name: 清晰的函数名称

    description: 函数功能的具体描述,尽量对输入参数和输出信息有明确的说明

    parameters: 对函数的每个输入参数进行类型定义及其描述

    required: 指定哪些参数必填

    具体结构函数如下所示:

    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": "string",
                                    "description": "中心点的经度",
                                },
                                "latitude": {
                                    "type": "string",
                                    "description": "中心点的纬度",
                                },
                                "keyword": {
                                    "type": "string",
                                    "description": "目标poi的关键字",
                                }
                            },
                            "required": ["longitude", "latitude", "keyword"],
                        }
                    }
                }
    

    注意:Function Calling 中的函数与参数的描述description也是一种 Prompt。这种 Prompt 也需要调优,否则会影响函数的召回、参数的准确性,甚至让 GPT 产生幻觉。

    定义本地函数

    • get_location_position 用于查询某个地点的地理坐标。

    • search_nearby_list 用于查询地理坐标附近的某些信息(取决于用户输入的Keyword)

    get_location_poinstion 代码块如下所示

    def get_location_position (location, city):
        url = f"https://restapi.amap.com/v5/place/text?key={amap_key}&keywords={location}&region={city}"
        print(url)
        r = requests.get(url)
        result = r.json()
        if "pois" in result and result["pois"]:
            return result["pois"][0]
        return None
    

    search_nearby_list 代码块如下所示

    def search_nearby_list (longitude, latitude, keyword):
        url = f"https://restapi.amap.com/v5/place/around?key={amap_key}&keywords={keyword}&location={longitude},{latitude}"
        print(url)
        r = requests.get(url)
        result = r.json()
        ans = ""
        if "pois" in result and result["pois"]:
            for i in range(min(3, len(result["pois"]))):
                name = result["pois"][i]["name"]
                address = result["pois"][i]["address"]
                distance = result["pois"][i]["distance"]
                ans += f"{name}\n{address}\n距离:{distance}米\n\n"
        return ans
    

    此处,利用的是高德地图的开放接口,在使用本例之前,需要先去高德地图开放接口的官网申请一个key

    高德地图map-key: https://console.amap.com/dev/user/permission

    数据传输关键点

    用户提问: “北京三里屯附近的咖啡馆”

    ChatGPT: {‘location’: ‘三里屯’, ‘city’: ‘北京’}

    get_location_coordinate作用是找到三里屯的精确位置,函数输入和输出分别是:

    输入数据为:{‘location’: ‘三里屯’, ‘city’: ‘北京’}

    输出为:

    可以看到,函数返回结果主要是和地区有关的,地名、地址、区号、代号、坐标等信息;

    {‘parent’: ‘’, ‘address’: ‘朝阳区’, ‘distance’: ‘’, ‘pcode’: ‘110000’, ‘adcode’: ‘110105’, ‘pname’: ‘北京市’, ‘cityname’: ‘北京市’, ‘type’: ‘地名地址信息;热点地名;热点地名’, ‘typecode’: ‘190700’, ‘adname’: ‘朝阳区’, ‘citycode’: ‘010’, ‘name’: ‘三里屯’, ‘location’: ‘116.455294,39.937492’, ‘id’: ‘B0FFF5BER7’}

    有用参数为经纬度坐标:‘location’: ‘116.455294,39.937492’

    search_nearby_pois作用是在指定坐标位置找出所需的目标,函数输入和输出分别是:

    输入:get_location_coordinate 的输出 + 原始Prompt;然后一起接入大模型GPT回答后的输出{‘longitude’: ‘116.455294’, ‘latitude’: ‘39.937492’, ‘keyword’: ‘咖啡馆’}

    即就是参数:longitude, latitude, keyword;

    输出为:

    输出结果单条如下所示:

    {“parent”:“B000A80TPS”,“address”:“三里屯路33号3.3大厦1层1010号”,“distance”:“52”,“pcode”:“110000”,“adcode”:“110105”,“pname”:“北京市”,“cityname”:“北京市”,“type”:“餐饮服务;咖啡厅;星巴克咖啡”,“typecode”:“050501”,“adname”:“朝阳区”,“citycode”:“010”,“name”:“星巴克咖啡(北京三里屯三点三大厦店)”,“location”:“116.455034,39.937060”,“id”:“B0G35RYBJW”}

    函数中,利用distance可以输出最近的N个店,或者输出小于阈值的店;

    根据需求定义输出标准,本例为:

    • “name”:" 星巴克咖啡(北京三里屯三点三大厦店)"
    • “distance”: “52”
    • “address”: “三里屯路33号3.3大厦1层1010号”

      本例输出为最近的3个(可自定义):

      1. 星巴克咖啡(北京三里屯三点三大厦店)

        地址:三里屯路33号3.3大厦1层1010号,距离52米

      2. 内山咖啡店(3•3大厦店)

        地址:三里屯路33号3•3大厦B1层,距离82米

      3. 春丽咖啡(3•3大厦店)

        地址:三里屯路33号3.3大厦东门1层1099,距离93米

      将输出结果再次append到Prompt,输入到大模型GPT 里,GPT答复输出整理后格式,如下所示。

      最终ChatGPT答复:

      ChatGPT: 根据您的要求,我找到了以下咖啡馆:
      1. 星巴克咖啡(北京三里屯三点三大厦店),地址:三里屯路33号3.3大厦1层1010号,距离52米。
      2. 内山咖啡店(3•3大厦店),地址:三里屯路33号3•3大厦B1层,距离82米。
      3. 春丽咖啡(3•3大厦店),地址:三里屯路33号3.3大厦东门1层1099,距离93米。
      以上是您附近的咖啡馆,您可以前往您喜欢的地方享用咖啡。
      Process finished with exit code 0
      

      整体实现思路

      (1)首先大模型识别到应该先调用get_location_coordinate函数获取经纬度;

      (2)get_location_coordinate执行结果给到大模型,大模型识别到下一步应该调用search_nearby_pois;

      (3)search_nearby_pois执行结果给到大模型,大模型识别到不需要调用其它函数,用自然语言组织了最终答案。

      总结

      • 将函数说明组织成json形式告诉大模型。其中最重要的函数和参数描述,是该函数的prompt,大模型通过这个描述来确定用户的输入是否匹配该函数,是否召回该函数。
      • 大模型如果召回了某个函数,即在本地去解析函数名和参数去使用,从而完成大模型与外部世界的连接。

        主要参考链接

        OpenAI官方Function Calling教程:

        https://platform.openai.com/docs/guides/function-calling

        最后,本地部署已经实现,是基于ChatGLM3b 实现的。有需要本地部署代码的私信。

The End
微信