函数调用,查找附近地点:利用谷歌地图 API 和客户档案

本笔记本的重点在于整合谷歌地图 API 和自定义用户档案,以增强基于位置的搜索。我们的方法结合使用谷歌地图 API 和用户偏好,旨在使地点发现更具个性化和相关性。请注意,虽然我们在此实例中侧重于谷歌地图 API,但还有许多其他 API 可供您以类似的方式进行探索和应用。

我们将探讨三个主要组件的应用:

  • 客户档案:此模拟档案捕获个人偏好,例如地点类型(例如,餐厅、公园、博物馆)、预算、首选评分和其他特定要求。

  • 谷歌地图 API:此 API 提供有关附近地点的实时数据。它会考虑您所在位置的各种数据点,例如评分、场地类型、成本等。

  • 函数调用:一个简单的命令,例如“我饿了”或“我想参观博物馆”,即可激活该函数,该函数结合了用户档案数据和谷歌地图 API 来识别合适的场地。

本笔记本介绍了两个主要用例:

  • 基于档案的推荐:了解如何创建用户档案并根据个人偏好进行地点推荐。

  • 使用函数调用进行 API 集成:了解如何有效集成和调用谷歌地图 API,以使用函数调用来获取各种地点的实时数据。

请注意,虽然此系统功能强大,但其有效性可能会因用户偏好和可用地点数据而异。在本笔记本中,客户数据是虚构的,位置是硬编码的。

设置

谷歌地图 API

要使用谷歌地图 API,您需要两样东西:

  • 谷歌帐户:如果您还没有,则需要创建一个谷歌帐户。

  • 谷歌地图 API 密钥:API 密钥是用于验证与您的项目相关的请求以用于使用和计费目的的唯一标识符。您可以从谷歌云控制台获取您的 API 密钥。

请注意,谷歌地图 API 是一项付费服务,费用与发出的 API 调用次数相关。请跟踪您的使用情况,以避免任何意外费用。

还需要 requests 库,您可以使用以下命令下载它:

pip install requests
import json
from openai import OpenAI
import os
import requests

client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "<your OpenAI API key if not set as env var>"))

在此代码段中,我们定义了一个名为 fetch_customer_profile 的函数,该函数接受 user_id 并返回一个模拟用户配置文件。

此函数模拟了一个从数据库获取用户数据的 API 调用。在本演示中,我们使用的是硬编码数据。用户配置文件包含各种详细信息,例如用户的地点(在此示例中设置为金门大桥的坐标)、食物和活动偏好、应用程序使用指标、最近的互动以及用户排名。

在生产环境中,您将用实际的 API 调用替换此硬编码数据以访问您的用户数据库。

def fetch_customer_profile(user_id):
    # 您可以在生产代码中替换此项以进行实际的 API 调用
    if user_id == "user1234":
        return {
            "name": "John Doe",
            "location": {
                "latitude": 37.7955,
                "longitude": -122.4026,
            },
            "preferences": {
                "food": ["Italian", "Sushi"],
                "activities": ["Hiking", "Reading"],
            },
            "behavioral_metrics": {
                "app_usage": {
                    "daily": 2,  # 小时
                    "weekly": 14  # 小时
                },
                "favourite_post_categories": ["Nature", "Food", "Books"],
                "active_time": "Evening",
            },
            "recent_searches": ["Italian restaurants nearby", "Book clubs"],
            "recent_interactions": ["Liked a post about 'Best Pizzas in New York'", "Commented on a post about 'Central Park Trails'"],
            "user_rank": "Gold",  # 基于某些内部排名系统
        }
    else:
        return None

请求和处理来自谷歌地图 API 的数据

call_google_places_api 函数用于从谷歌地图 API 请求信息,并根据给定的 place_type 和可选的 food_preference 提供前两个地点的列表。我们将此函数限制为前两个结果,以控制使用量,因为这是一项付费服务。但是,您可以根据需要修改此设置以检索任意数量的结果。

该函数配置为使用硬编码的位置(设置为泛美金字塔的坐标)、您的谷歌 API 密钥以及特定的请求参数。根据 place_type,它会形成适当的 API 请求 URL。如果 place_type 是餐厅并且指定了 food_preference,则会将其包含在 API 请求中。

发送 GET 请求后,该函数会检查响应状态。如果成功,它将使用 get_place_details 函数处理 JSON 响应,提取相关详细信息,并以人类可读的格式返回它们。如果请求失败,它会打印出错误信息以进行调试。

get_place_details 函数用于检索有关某个地点的更详细信息,前提是提供了其 place_id。它向谷歌地点详情 API 发送 GET 请求,并在请求成功时返回结果。如果请求失败,它会打印出错误信息以进行调试。

这两个函数都处理异常,并在出现问题时返回错误消息。

def get_place_details(place_id, api_key):
    URL = f"https://maps.googleapis.com/maps/api/place/details/json?place_id={place_id}&key={api_key}"
    response = requests.get(URL)
    if response.status_code == 200:
        result = json.loads(response.content)["result"]
        return result
    else:
        print(f"Google Place Details API request failed with status code {response.status_code}")
        print(f"Response content: {response.content}")
        return None
def call_google_places_api(user_id, place_type, food_preference=None):
    try:
        # 获取客户档案
        customer_profile = fetch_customer_profile(user_id)
        if customer_profile is None:
            return "我找不到您的个人资料。您能核实一下您的用户 ID 吗?"

        # 从客户档案中获取位置
        lat = customer_profile["location"]["latitude"]
        lng = customer_profile["location"]["longitude"]

        API_KEY = os.getenv('GOOGLE_PLACES_API_KEY')  # 从环境变量中检索 API 密钥
        LOCATION = f"{lat},{lng}"
        RADIUS = 500  # 在 500 米半径内搜索
        TYPE = place_type

        # 如果 place_type 是 restaurant 且 food_preference 不为 None,则将其包含在 API 请求中
        if place_type == 'restaurant' and food_preference:
            URL = f"https://maps.googleapis.com/maps/api/place/nearbysearch/json?location={LOCATION}&radius={RADIUS}&type={TYPE}&keyword={food_preference}&key={API_KEY}"
        else:
            URL = f"https://maps.googleapis.com/maps/api/place/nearbysearch/json?location={LOCATION}&radius={RADIUS}&type={TYPE}&key={API_KEY}"

        response = requests.get(URL)
        if response.status_code == 200:
            results = json.loads(response.content)["results"]
            places = []
            for place in results[:2]:  # 限制为前 2 个结果
                place_id = place.get("place_id")
                place_details = get_place_details(place_id, API_KEY)  # 获取地点的详细信息

                place_name = place_details.get("name", "N/A")
                place_types = next((t for t in place_details.get("types", []) if t not in ["food", "point_of_interest"]), "N/A")  # 获取地点的第一个类型,排除 "food" 和 "point_of_interest"
                place_rating = place_details.get("rating", "N/A")  # 获取地点的评分
                total_ratings = place_details.get("user_ratings_total", "N/A")  # 获取评分总数
                place_address = place_details.get("vicinity", "N/A")  # 获取地点的邻近区域

                if ',' in place_address:  # 如果地址包含逗号
                    street_address = place_address.split(',')[0]  # 按逗号分割并只保留第一部分
                else:
                    street_address = place_address

                # 为此地点准备输出字符串
                place_info = f"{place_name} 是一家位于 {street_address} 的 {place_types}。根据 {total_ratings} 条用户评论,它的评分为 {place_rating}。"

                places.append(place_info)

            return places
        else:
            print(f"Google Places API request failed with status code {response.status_code}")
            print(f"Response content: {response.content}")  # 打印响应内容以进行调试
            return []
    except Exception as e:
        print(f"Error during the Google Places API call: {e}")
        return []

使用 GPT-3.5-Turbo 和谷歌地图 API 生成用户特定推荐

provide_user_specific_recommendations 函数与 GPT-3.5-Turbo 和谷歌地图 API 进行交互,以提供针对用户偏好和位置量身定制的响应。

首先,它使用用户的 user_id 获取客户的个人资料。如果找不到个人资料,它将返回一条错误消息。

在获得有效个人资料后,它会提取客户的食物偏好,然后与 OpenAI 模型进行交互。它提供了一个初始系统消息,为 AI 模型提供有关其角色、用户偏好和谷歌地图 API 函数使用情况的上下文。

用户输入也作为消息发送给模型,并且 call_google_places_api 函数在 functions 参数中定义,供 AI 模型根据需要调用。

最后,它处理模型的响应。如果模型调用了谷歌地图 API 的函数,则会使用适当的参数执行该函数,并返回附近地点的名称。如果没有此类地点或请求未被理解,则会返回适当的错误消息。

def provide_user_specific_recommendations(user_input, user_id):
    customer_profile = fetch_customer_profile(user_id)
    if customer_profile is None:
        return "我找不到您的个人资料。您能核实一下您的用户 ID 吗?"

    customer_profile_str = json.dumps(customer_profile)

    food_preference = customer_profile.get('preferences', {}).get('food', [])[0] if customer_profile.get('preferences', {}).get('food') else None


    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
    {
        "role": "system",
        "content": f"您是一位经验丰富的 AI 助手,是用户意图检测和解释方面的专家。您的任务是感知并响应用户的需求,即使这些需求是以间接或直接的方式表达的。您擅长识别细微的线索:例如,如果用户表示他们“饿了”,您应该假设他们正在寻找附近的餐饮场所,如餐厅或咖啡馆。如果他们感觉“疲倦”、“劳累”或提到长途旅行,请将其解释为对住宿地点(如酒店或宾馆)的请求。但是,请记住要把握好解释和假设的界限:如果用户的意图不明确或可以有多种解释方式,请随时礼貌地请求更多澄清。确保根据用户偏好和过往经验(可在 {customer_profile_str} 中找到)来定制您的回复。"
    },
    {"role": "user", "content": user_input}
],
        temperature=0,
        tools=[
            {
                "type": "function",
                "function" : {
                    "name": "call_google_places_api",
                    "description": "此函数调用谷歌地图 API 来查找特定位置附近指定类型的顶级地点。当用户表达需求(例如,感到饥饿或疲倦)或想查找特定类型的地点(例如,餐厅或酒店)时,可以使用此函数。",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "place_type": {
                                "type": "string",
                                "description": "要搜索的地点类型。"
                            }
                        }
                    },
                    "result": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    }
                }
            }
        ],
    )

    print(response.choices[0].message.tool_calls)

    if response.choices[0].finish_reason=='tool_calls':
        function_call = response.choices[0].message.tool_calls[0].function
        if function_call.name == "call_google_places_api":
            place_type = json.loads(function_call.arguments)["place_type"]
            places = call_google_places_api(user_id, place_type, food_preference)
            if places:  # 如果地点列表不为空
                return f"以下是一些您可能会感兴趣的地方:{' '.join(places)}"
            else:
                return "我找不到附近感兴趣的地点。"

    return "抱歉,我无法理解您的请求。"

执行用户特定推荐

执行后,该函数将获取用户的个人资料,与 AI 模型进行交互,处理模型的响应,在必要时调用谷歌地图 API,并最终返回根据用户偏好和位置量身定制的推荐列表。打印的输出将包括这些个性化推荐。

user_id = "user1234"
user_input = "I'm hungry"
output = provide_user_specific_recommendations(user_input, user_id)
print(output)
[ChatCompletionMessageToolCall(id='call_Q1mXIi7D6GhobfE4tkruX7nB', function=Function(arguments='{\n  "place_type": "restaurant"\n}', name='call_google_places_api'), type='function')]
以下是一些您可能会感兴趣的地方:Sotto Mare 是一家位于 552 Green Street 的餐厅。根据 3765 条用户评论,它的评分为 4.6。Mona Lisa Restaurant 是一家位于 353 Columbus Avenue #3907 的餐厅。根据 1888 条用户评论,它的评分为 4.4。