基于大语言模型与API集成的智能旅行助手开发实践

📅 2026/8/3 7:25:42 👁️ 阅读次数 📝 编程学习
基于大语言模型与API集成的智能旅行助手开发实践

这类工具最值得先看的不是功能列表,而是能不能在普通环境里稳定跑起来,以及它到底解决了什么具体问题。Gemini 接入 Viator 这个主题,乍一看像是两个服务的集成,但核心要解决的问题其实很明确:如何通过一个统一的、智能化的对话界面,来查询、筛选和预订全球的旅行活动,而不是在多个网站和 App 之间来回切换、比价、看评论。

它适合两类人看:一类是经常需要规划行程、寻找特色体验的旅行者,另一类是对 AI 应用集成、API 调用和自动化流程感兴趣的开发者。对于旅行者,最关键的价值是省去了信息搜集和决策的繁琐过程;对于开发者,最值得关注的点是如何将大语言模型的自然语言理解能力,与一个结构化的商业服务 API 进行可靠、安全的对接。

下面我会按实际落地顺序拆一遍,从理解集成原理、准备环境、模拟请求,到处理真实预订流程中的边界情况。

1. 先拆解“接入”到底意味着什么:是界面整合还是 API 调用?

看到“Gemini 接入 Viator”,很多人第一反应是有一个现成的、开箱即用的产品。但在实际动手前,必须明确一个关键点:目前并没有一个官方发布的、名为“Gemini for Viator”的独立应用。这里的“接入”更可能指的是一种技术实现模式或潜在的应用场景。

1.1 两种主流的“接入”实现方式

通常,这类集成有两种实现路径:

  1. 前端界面整合:开发一个 Web 或移动应用,界面是类似 Gemini 的聊天对话框。用户用自然语言提问(如“下周末在东京晚上有什么独特的文化体验,预算每人1万日元左右?”),后端同时处理两项任务:

    • 将用户问题发送给 Gemini 这类大语言模型的 API,让其理解意图、提取关键筛选条件(地点:东京,时间:下周末晚上,类型:文化体验,预算:~1万日元)。
    • 将这些结构化的条件,转换为对 Viator API 的搜索请求,获取活动列表。
    • 最后,将 Viator API 返回的结构化数据(活动名称、价格、评分、简介),再用 Gemini API 组织成一段流畅、带总结和推荐的自然语言回复,呈现给用户。
  2. 自动化脚本/工作流:对于开发者或高级用户,可能是编写一个脚本(Python 等)。这个脚本同样接收自然语言指令,通过代码调用 Gemini API 来解析指令,再调用 Viator API 获取数据,最终可能以格式化文本、邮件或日历事件的形式输出结果。

现阶段,对于大多数想体验的用户来说,直接可用的成品较少,更多需要基于双方提供的 API 自行搭建。因此,本文后续将聚焦于第二种方式,即从开发者的角度,讲解如何利用 Gemini API 和 Viator API 构建一个简单的旅行活动查询工具。这是理解整个集成逻辑最直接的方法。

1.2 你需要准备的核心资源

在开始写代码之前,你需要先申请好两个关键资源:

  • Gemini API 密钥:这是调用 Google Gemini 模型能力的凭证。你需要前往 Google AI Studio (makersuite.google.com) 注册并创建 API Key。注意其可用区域和配额限制。
  • Viator API 密钥:这是访问 Viator 活动数据的凭证。你需要前往 Viator 的合作伙伴门户(通常是partner.viator.com)注册为开发者,申请 API 访问权限。Viator API 通常是商业性质的,可能需要联系其销售或合作伙伴团队,并了解其费用结构、请求限制(Rate Limits)和使用条款。

重要提示:获取这两个密钥,特别是 Viator 的,可能需要一定时间,并且可能涉及商业审核。对于初步学习和测试,你可以先用 Gemini API 配合一个模拟的或本地的“活动数据”来验证核心逻辑。

2. 环境搭建与最小可行性验证

不要一上来就想做一个完整的预订系统。我建议先从最小可行性验证开始:让程序能理解一句简单的旅行查询,并返回一段固定的、模拟的 Viator 活动信息。

2.1 基础 Python 环境配置

假设我们使用 Python 作为开发语言。首先创建一个干净的虚拟环境并安装必要依赖。

# 创建并激活虚拟环境(以 venv 为例) python -m venv venv_gemini_viator # Windows: venv_gemini_viator\Scripts\activate # macOS/Linux: source venv_gemini_viator/bin/activate # 安装核心库 pip install google-generativeai requests python-dotenv
  • google-generativeai: Google 官方提供的 Gemini API Python SDK。
  • requests: 用于后续发起 HTTP 请求调用 Viator API(在模拟阶段也会用到)。
  • python-dotenv: 用于安全地管理环境变量和 API 密钥。

2.2 安全存储你的 API 密钥

永远不要将 API 密钥硬编码在代码中。创建一个.env文件来存储它们:

# .env 文件内容 GEMINI_API_KEY=你的_Gemini_API_密钥 # VIATOR_API_KEY=你的_Viator_API_密钥 (暂时注释掉,我们先模拟)

然后在代码中通过dotenv加载:

import os from dotenv import load_dotenv load_dotenv() # 加载 .env 文件中的环境变量 GEMINI_API_KEY = os.getenv('GEMINI_API_KEY') # VIATOR_API_KEY = os.getenv('VIATOR_API_KEY')

2.3 第一步:让 Gemini 理解用户意图

我们先不连接 Viator,只测试 Gemini 能否从用户的自然语言中准确提取出结构化的搜索参数。

import google.generativeai as genai # 配置 Gemini API genai.configure(api_key=GEMINI_API_KEY) # 选择一个模型,例如 gemini-1.5-pro model = genai.GenerativeModel('gemini-1.5-pro') def parse_travel_query(user_query): """ 使用 Gemini 解析用户旅行查询,提取结构化参数。 """ prompt = f""" 你是一个旅行助手。请从用户的以下查询中,提取出用于搜索旅行活动的关键参数。 请以 JSON 格式返回,且只返回 JSON,不要有其他解释。 需要的字段包括: - destination (城市或国家,如 “Paris, France”) - start_date (YYYY-MM-DD 格式,如果未指定则返回 null) - end_date (YYYY-MM-DD 格式,如果未指定则返回 null) - category (活动类别,如 “food”, “culture”, “adventure”, “sightseeing”) - budget_per_person (人均预算数字,货币单位统一为 USD,如果未指定则返回 null) - keywords (其他关键词列表,如 [“night tour”, “small group”]) 用户查询:{user_query} """ try: response = model.generate_content(prompt) # 假设返回的是纯 JSON 文本 json_str = response.text.strip() # 在实际应用中,这里需要更健壮的 JSON 解析和错误处理 print("Gemini 解析出的参数:", json_str) return json_str except Exception as e: print(f"调用 Gemini API 出错:{e}") return None # 测试一下 if __name__ == "__main__": test_query = "我想下个月15号在罗马参加一个美食步行团,最好是小团,预算每人50欧左右。" result = parse_travel_query(test_query)

运行这段代码,如果 Gemini API 配置正确,你应该会看到它输出一段 JSON,类似:{"destination": "Rome, Italy", "start_date": "2024-07-15", "end_date": null, "category": "food", "budget_per_person": 50, "keywords": ["walking tour", "small group"]}

关键点:这里我们通过精心设计的 Prompt(提示词),引导 Gemini 输出结构化的数据。Prompt 工程是这类应用的核心,你需要不断调整它以适应更复杂、更模糊的用户输入。

3. 模拟 Viator API 响应与集成测试

拿到结构化的搜索参数后,下一步本该是调用 Viator API。但在没有正式 API 密钥的测试阶段,我们可以先模拟一个 Viator API 的响应。

3.1 构建一个模拟的 Viator API 客户端

我们创建一个函数,接收上一步解析出的参数,返回一个模拟的活动列表。

import json from datetime import datetime def mock_viator_search(search_params): """ 模拟 Viator API 的搜索功能。 search_params: 从 parse_travel_query 返回的 JSON 字符串或字典。 """ if isinstance(search_params, str): params = json.loads(search_params) else: params = search_params destination = params.get('destination', 'Unknown') category = params.get('category', '') # 基于目的地和类别,返回一些模拟活动 mock_activities = [ { "productCode": "MOCK-001", "title": f"Authentic {category.capitalize()} Experience in {destination.split(',')[0]}", "description": f"A wonderful small-group {category} tour exploring the hidden gems.", "price": {"amount": 45.00, "currency": "USD"}, "rating": 4.8, "reviewCount": 127, "duration": "3 hours", "bookingLink": f"https://www.viator.com/mock-link-1" }, { "productCode": "MOCK-002", "title": f"Premium {category.capitalize()} Adventure in {destination.split(',')[0]}", "description": f"An exclusive tour with an expert guide, limited to 10 people.", "price": {"amount": 65.00, "currency": "USD"}, "rating": 4.9, "reviewCount": 89, "duration": "4.5 hours", "bookingLink": f"https://www.viator.com/mock-link-2" }, ] # 简单模拟一下预算过滤 budget = params.get('budget_per_person') if budget: mock_activities = [act for act in mock_activities if act['price']['amount'] <= budget] return mock_activities # 测试集成 if __name__ == "__main__": # 假设这是 Gemini 解析的结果 parsed_params = '{"destination": "Rome, Italy", "start_date": "2024-07-15", "category": "food", "budget_per_person": 50}' activities = mock_viator_search(parsed_params) print("模拟搜索到的活动:", json.dumps(activities, indent=2, ensure_ascii=False))

3.2 让 Gemini 生成人性化的回复

现在我们有结构化的活动数据了,但直接把这个 JSON 扔给用户很不友好。最后一步,再次调用 Gemini,让它根据活动数据生成一段推荐文字。

def generate_recommendation(user_query, activities_list): """ 使用 Gemini 将活动列表转化为自然语言推荐。 """ activities_str = json.dumps(activities_list, ensure_ascii=False) prompt = f""" 你是一个专业的旅行顾问。一位用户提出了以下需求: “{user_query}” 你已根据需求,找到了以下活动选项: {activities_str} 请根据这些信息,为用户生成一段友好、有帮助的回复。回复需要: 1. 总结用户的查询条件。 2. 简要介绍你找到的每个活动(突出亮点,如高评分、符合预算、特色描述)。 3. 给出一个初步的推荐建议。 4. 最后提醒用户,可以通过提供的链接查看详情和预订。 请使用中文回复。 """ try: response = model.generate_content(prompt) return response.text except Exception as e: print(f"生成推荐时出错:{e}") return "抱歉,暂时无法生成推荐。" # 完整的流程测试 if __name__ == "__main__": user_input = "我想下个月15号在罗马参加一个美食步行团,最好是小团,预算每人50欧左右。" print(f"用户查询:{user_input}") # 1. 解析查询 params = parse_travel_query(user_input) if not params: print("无法解析查询。") exit() # 2. 搜索活动(模拟) activities = mock_viator_search(params) if not activities: final_response = f"根据您的条件({json.loads(params)}),暂时没有找到合适的活动。建议您调整搜索条件,例如放宽预算或选择其他日期。" else: # 3. 生成推荐 final_response = generate_recommendation(user_input, activities) print("\n" + "="*50) print("旅行助手回复:") print("="*50) print(final_response)

运行这个完整的流程,你会看到一个端到端的演示:用户输入自然语言 -> Gemini 解析 ->(模拟)搜索活动 -> Gemini 生成推荐回复。这验证了集成的核心逻辑是可行的。

4. 连接真实的 Viator API 与生产化考量

模拟测试通过后,下一步就是替换掉mock_viator_search函数,接入真实的 Viator API。

4.1 了解 Viator API 的基本使用

Viator API 是 RESTful 风格的。你需要查阅其官方文档,找到“产品搜索”相关的端点(Endpoint)。一个典型的搜索请求可能如下:

import requests def real_viator_search(search_params, viator_api_key, api_base_url="https://api.viator.com/partner"): """ 调用真实的 Viator API 进行搜索。 注意:此函数为示例,实际参数和端点需以 Viator 官方文档为准。 """ headers = { "Accept": "application/json", "Accept-Language": "en-US", "Authorization": f"Bearer {viator_api_key}" # 或可能是其他认证方式 } # 将我们解析的参数映射到 Viator API 所需的参数 query_params = { "destId": convert_destination_to_dest_id(search_params['destination']), # 需要目的地ID映射 "startDate": search_params.get('start_date'), "endDate": search_params.get('end_date'), "categories": map_category_to_viator_code(search_params.get('category')), # 需要类别映射 "sortOrder": "REVIEW_AVG_RATING_D", "topX": "10", "currency": "USD" } # 移除空值参数 query_params = {k: v for k, v in query_params.items() if v is not None} try: response = requests.get(f"{api_base_url}/products/search", headers=headers, params=query_params, timeout=10) response.raise_for_status() # 检查 HTTP 错误 data = response.json() # 从返回的 data 中提取活动列表,并格式化成我们需要的结构 return format_viator_activities(data.get('products', [])) except requests.exceptions.RequestException as e: print(f"调用 Viator API 失败:{e}") return []

这里有几个关键挑战:

  1. 参数映射:Viator API 通常使用内部的目的地 ID (destId) 和分类代码,而不是城市名称字符串。你需要维护一个映射表,或者调用其“目的地自动补全”等辅助 API 来动态获取 ID。
  2. 认证:Viator API 的认证方式(如 OAuth, API Key)需严格按文档实现。
  3. 错误处理:网络超时、API 限流、无效参数等都需要妥善处理,避免程序崩溃。
  4. 数据格式化:Viator API 返回的数据结构可能很复杂,需要编写format_viator_activities函数来提取我们关心的字段(标题、价格、评分、链接等)。

4.2 生产环境必须考虑的要点

如果计划将这个工具用于实际服务,以下问题不能忽略:

  • 成本控制:Gemini API 和 Viator API 都可能按调用次数收费。你需要实现缓存机制(例如,对相同的搜索条件,在一定时间内缓存结果),并设置用量监控和告警。
  • 用户体验与安全
    • 超时处理:设置合理的 API 调用超时(如 Gemini 5秒,Viator 8秒),并准备超时后的降级回复(如“正在努力查询,请稍后再试”或返回缓存结果)。
    • 输入清洗与安全:对用户的输入进行基本的清洗,防止 Prompt 注入攻击。避免将未经处理的用户输入直接拼接进发送给 Gemini 的 Prompt。
    • 免责声明:在回复中明确告知用户,信息来源于 Viator,并由 AI 进行整理,建议用户预订前在 Viator 官网核实最新详情、价格和可用性。
  • 合规与条款:仔细阅读 Gemini API 和 Viator API 的使用条款。确保你的使用场景(特别是涉及商业用途、数据展示和转发)符合其规定。绝对不要尝试绕过任何 API 的调用限制或进行未经授权的抓取。

5. 常见问题排查与调试思路

在实际开发中,你肯定会遇到各种问题。下面是一个典型的排查顺序:

5.1 Gemini API 调用失败

  • 现象google.generativeai库抛出认证错误或权限错误。
  • 排查
    1. 检查 API 密钥:确认GEMINI_API_KEY环境变量已正确加载,且密钥未过期、未禁用。可以在命令行用echo $GEMINI_API_KEY(Linux/macOS) 或echo %GEMINI_API_KEY%(Windows) 快速验证。
    2. 检查网络连接:确保你的运行环境可以访问generativelanguage.googleapis.com。临时关闭代理或防火墙试试。
    3. 检查配额:前往 Google AI Studio 控制台,查看 API 调用次数配额是否已用尽。
    4. 简化测试:写一个最简单的脚本,只调用model.generate_content(“Hello”),排除其他代码干扰。

5.2 Gemini 解析结果不符合预期

  • 现象:返回的不是纯 JSON,或者提取的参数错误。
  • 排查
    1. 优化 Prompt:这是最常见的原因。在 Prompt 中更明确地指定输出格式(如“输出必须是合法的 JSON,且只包含以下字段……”)。可以要求 Gemini 在思考时先输出一个“思考过程”,但最终只返回 JSON。
    2. 使用结构化输出:如果 Gemini 版本支持(如gemini-1.5-proresponse.candidates[0].content.parts[0]可能包含结构化数据),优先使用该功能。
    3. 后处理清洗:对返回的文本进行后处理,例如用json.loads()尝试解析,如果失败,则尝试用正则表达式提取{...}之间的内容。
    4. 调整温度参数:在调用generate_content时,设置generation_config=genai.GenerationConfig(temperature=0.1),降低随机性,使输出更稳定。

5.3 Viator API 返回空结果或错误

  • 现象real_viator_search返回空列表或 HTTP 错误码。
  • 排查
    1. 验证认证:先用一个最简单的请求(如获取目的地列表)测试 API 密钥和认证头是否正确。
    2. 打印请求详情:在开发阶段,将构建好的最终请求 URL 和 Headers 打印出来,用curl或 Postman 手动测试,确认参数无误。
    3. 检查参数映射:重点检查destId和分类代码。确认你的映射函数convert_destination_to_dest_id能正确工作。Viator 可能提供“目的地搜索”API 来动态查询 ID。
    4. 查看 API 响应:即使返回 HTTP 200,也要仔细查看响应体。可能包含“error”: “NO_PRODUCTS_AVAILABLE”之类的业务逻辑错误信息。
    5. 遵守限流:查看 Viator API 文档的 Rate Limiting 部分,确保你的调用频率没有超标。

5.4 整体流程响应慢

  • 现象:从用户输入到最终回复耗时超过 10 秒。
  • 优化
    1. 并行调用:如果流程允许,在解析出用户参数后,可以并行发起对 Gemini(用于生成推荐)和 Viator API(用于获取数据)的调用,而不是串行。
    2. 实现缓存:对“查询参数 -> Viator 搜索结果”建立缓存(如使用 Redis 或内存缓存,缓存 5-10 分钟)。对于热门目的地和常见查询,能极大提升响应速度并降低成本。
    3. 超时设置:为两个 API 调用设置独立的、合理的超时时间。如果 Viator API 超时,可以尝试使用缓存的结果,或者直接返回一个友好的提示,而不是让用户长时间等待。

6. 扩展思路与边界探讨

这个基础框架可以朝多个方向扩展,但每个方向都有其边界和挑战。

6.1 从“查询”到“预订”

真正的“预订”涉及复杂的流程,远非一个 API 调用那么简单:

  • 可用性检查:需要调用 Viator API 检查特定日期、人数的库存。
  • 价格明细:获取含税、含费的总价。
  • 用户数据收集:需要安全地收集参与者姓名、联系方式等敏感信息。绝对不要用 Gemini 等 AI 模型来传输或暂存用户的个人身份信息或支付信息。
  • 支付集成:这需要接入支付网关,并处理 PCI DSS 合规等复杂问题。
  • 订单确认与凭证:处理订单确认、发送电子票等。

因此,一个完整的预订机器人或助手,其技术复杂度和合规要求非常高。对于个人开发者或小团队,更现实的路径是止步于“智能推荐与引流”:即生成推荐并提供直达 Viator 官方预订页面的链接,将后续复杂的预订流程交给 Viator 自身的平台完成。

6.2 支持更复杂的查询

用户的查询可能非常模糊,例如“我暑假带小孩去伦敦,有什么好玩的?”。

  • 多轮对话:需要维护对话上下文。你可以将历史对话记录也作为 Prompt 的一部分发送给 Gemini,让它理解“暑假”可能指七八月,“小孩”意味着需要筛选家庭友好型活动。
  • 意图消歧:用户可能在同一句话里混合多个意图(“找个地方吃饭然后看夜景”)。需要更复杂的 Prompt 或甚至使用 Agent 架构,让 AI 主动提问澄清(“您是想找一个既能用餐又能观景的场所,还是分别推荐餐厅和观景台?”)。

6.3 与其他服务集成

除了 Viator,还可以考虑集成其他旅行服务 API,如 Booking.com(住宿)、Skyscanner(航班)、Google Maps(路线)。这会让助手更强大,但也带来新问题:

  • 多 API 协调:如何并行或按顺序调用多个 API,并合并结果?
  • 统一回复:如何将来自不同源的数据(航班时间、酒店价格、活动评分)整合成一段连贯的推荐?
  • 成本与复杂度激增:每个 API 都有其学习成本、认证流程、调用限制和费用模型。

我个人更建议先把单服务(Viator)的查询和推荐做稳定、做流畅。在核心流程(解析 -> 搜索 -> 生成回复)能稳定运行且响应迅速的基础上,再考虑增加一个其他数据源。贪多求全往往会导致每个环节都不稳定。

最后,这类项目真正落地时,最该盯住的不是功能列表有多长,而是输入解析的准确性、API 调用的稳定性、异常情况的处理能力以及清晰的用户预期管理。告诉用户 AI 能做什么(快速筛选和总结信息),不能做什么(直接完成支付和出具票据),才能构建一个真正有用且可靠的数字助手。