跳到主要内容

调用附近地点的功能:利用Google Places API和客户个人资料

nbviewer

本笔记本围绕着整合Google Places API和自定义用户个人资料来增强基于位置的搜索展开。我们的方法涉及使用Google Places API结合用户偏好,旨在使位置发现更加个性化和相关。请注意,虽然我们在这个示例中专注于Google Places API,但你也可以探索和类似方式应用许多其他API。

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

  • 客户个人资料:这个模拟个人资料捕捉了个人对地点类型(例如餐厅、公园、博物馆)、预算、偏好评级和其他特定要求的偏好。

  • Google Places API:这个API提供附近地点的实时数据。它考虑了来自你周围地点的各种数据点,如评级、场所类型、成本等。

  • 函数调用:一个简单的命令,比如“我饿了”或“我想参观博物馆”,会激活该函数,结合用户个人资料数据和Google Places API来识别合适的场所。

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

  • 基于个人资料的推荐:学习如何创建用户个人资料,并根据个人偏好进行地点推荐。

  • API集成与函数调用:了解如何有效集成和调用Google Places API,以使用函数调用获取各种地点的实时数据。

请注意,虽然这个系统非常灵活,但其有效性可能会根据用户偏好和可用地点数据而变化。在本笔记本中,客户数据是虚构的,地点是硬编码的。

设置

Google Places API

要使用Google Places API,您需要两样东西:

  • Google账号:如果您还没有Google账号,您需要创建一个Google账号。

  • Google Places API密钥:API密钥是用于验证与您的项目相关的请求以进行使用和计费目的的唯一标识符。您可以从Google Cloud控制台获取您的API密钥。

请注意,Google Places API是一个付费服务,费用与所发出的API调用次数相关。跟踪您的使用情况以避免任何意外费用。

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

pip install requests

::: {.cell execution_count=1}
``` {.python .cell-code}
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


请求和处理来自Google Places API的数据

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

该函数配置了一个硬编码的位置(设置为Transamerica Pyramid的坐标)、您的Google API密钥和特定的请求参数。根据place_type,它构建适当的API请求URL。如果place_type是一个餐厅并且指定了food_preference,则它将包含在API请求中。

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

get_place_details函数用于根据其place_id检索有关地点的更详细信息。它向Google Place Details 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 "I couldn't find your profile. Could you please verify your user ID?"

# Get location from customer profile
lat = customer_profile["location"]["latitude"]
lng = customer_profile["location"]["longitude"]

API_KEY = os.getenv('GOOGLE_PLACES_API_KEY') # retrieve API key from environment variable
LOCATION = f"{lat},{lng}"
RADIUS = 500 # search within a radius of 500 meters
TYPE = place_type

# If the place_type is restaurant and food_preference is not None, include it in the API request
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]: # limit to top 2 results
place_id = place.get("place_id")
place_details = get_place_details(place_id, API_KEY) # Get the details of the place

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") # Get the first type of the place, excluding "food" and "point_of_interest"
place_rating = place_details.get("rating", "N/A") # Get the rating of the place
total_ratings = place_details.get("user_ratings_total", "N/A") # Get the total number of ratings
place_address = place_details.get("vicinity", "N/A") # Get the vicinity of the place

if ',' in place_address: # If the address contains a comma
street_address = place_address.split(',')[0] # Split by comma and keep only the first part
else:
street_address = place_address

# Prepare the output string for this place
place_info = f"{place_name} is a {place_types} located at {street_address}. It has a rating of {place_rating} based on {total_ratings} user reviews."

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}") # print out the response content for debugging
return []
except Exception as e:
print(f"Error during the Google Places API call: {e}")
return []


使用GPT-3.5-Turbo和Google Places API生成用户特定推荐

函数provide_user_specific_recommendations与GPT-3.5-Turbo和Google Places API交互,以提供根据用户偏好和位置定制的响应。

首先,它使用他们的user_id获取客户的个人资料。如果未找到个人资料,则返回错误消息。

有了有效的个人资料,它提取客户的食品偏好,然后与OpenAI模型交互。它提供一个初始系统消息,向AI模型提供关于其角色、用户偏好以及使用Google Places API功能的上下文。

用户输入也作为消息发送到模型,函数call_google_places_apifunctions参数中被定义,供AI模型根据需要调用。

最后,它处理模型的响应。如果模型调用Google Places API的函数,则使用适当的参数执行该函数,并返回附近地点的名称。如果没有这样的地点或请求不被理解,则返回适当的错误消息。

def provide_user_specific_recommendations(user_input, user_id):
customer_profile = fetch_customer_profile(user_id)
if customer_profile is None:
return "I couldn't find your profile. Could you please verify your user 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"You are a sophisticated AI assistant, a specialist in user intent detection and interpretation. Your task is to perceive and respond to the user's needs, even when they're expressed in an indirect or direct manner. You excel in recognizing subtle cues: for example, if a user states they are 'hungry', you should assume they are seeking nearby dining options such as a restaurant or a cafe. If they indicate feeling 'tired', 'weary', or mention a long journey, interpret this as a request for accommodation options like hotels or guest houses. However, remember to navigate the fine line of interpretation and assumption: if a user's intent is unclear or can be interpreted in multiple ways, do not hesitate to politely ask for additional clarification. Make sure to tailor your responses to the user based on their preferences and past experiences which can be found here {customer_profile_str}"
},
{"role": "user", "content": user_input}
],
temperature=0,
tools=[
{
"type": "function",
"function" : {
"name": "call_google_places_api",
"description": "This function calls the Google Places API to find the top places of a specified type near a specific location. It can be used when a user expresses a need (e.g., feeling hungry or tired) or wants to find a certain type of place (e.g., restaurant or hotel).",
"parameters": {
"type": "object",
"properties": {
"place_type": {
"type": "string",
"description": "The type of place to search for."
}
}
},
"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"Here are some places you might be interested in: {' '.join(places)}"
else:
return "I couldn't find any places of interest nearby."

return "I am sorry, but I could not understand your request."



执行用户特定的推荐

在执行时,该函数会获取用户的个人资料,与AI模型交互,处理模型的响应,必要时调用Google Places 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')]
Here are some places you might be interested in: Sotto Mare is a restaurant located at 552 Green Street. It has a rating of 4.6 based on 3765 user reviews. Mona Lisa Restaurant is a restaurant located at 353 Columbus Avenue #3907. It has a rating of 4.4 based on 1888 user reviews.