前阵子刷到 Google AI Mode 更新机票价格追踪与酒店预订功能的消息,不少人第一反应是“AI 搜索终于开始管落地的事了”。以前我们用搜索引擎查机票、比酒店,本质还是“拿到一堆链接自己点开看”;现在 AI 模式能直接拆解需求、盯住价格变化、甚至把预订流程串起来。这个方向对做搜索产品、AI Agent、旅游服务平台的技术同学都有参考价值。今天这篇文章不打算只停留在新闻层面,而是从功能拆解、技术原理、数据模型、本地模拟实现几个角度来完整还原这套能力背后的设计思路,读完你可以照着把原型跑起来。
1. 背景:AI 搜索正在从“找信息”变成“办事情”
1.1 传统搜索与 AI 行动的差别
传统搜索引擎的工作方式可以概括为“召回 + 排序”。你输入“北京到上海机票”,搜索引擎根据关键词匹配出大量网页,再由你人工完成比价、选航班、跳转到第三方平台下单。整个过程里,搜索引擎只负责“导航”,不负责“理解”和“执行”。
Google AI Mode 的定位则是“AI 代理式搜索”。它不只是给链接,而是尝试直接回答完整问题,并且在涉及具体任务时,把任务拆成步骤去执行。机票价格追踪和酒店预订,是 AI Mode 从“回答型”走向“行动型”的两个典型场景。价格追踪意味着系统具备时间维度上的数据监控能力,酒店预订意味着系统具备服务闭环能力。
1.2 为什么先做机票和酒店
机票和酒店天然适合 AI 代理介入,原因有三个:
- 信息结构化程度高:航班号、日期、价格、舱位、酒店房型、入住退房日期都是标准字段。
- 决策链路长:用户通常要反复比价、关注价格波动、确认退改政策。
- 预订动作明确:与“搜资料写文章”不同,买票订房的终端动作是明确的交易行为。
所以,Google 选择在这两个场景上落地 AI 行动能力,既有用户价值,也有技术上的可行性基础。
1.3 本文你能获得什么
- 理解 Google AI Mode 新增功能的定位与产品逻辑。
- 掌握价格追踪系统的数据模型与实现思路。
- 掌握酒店预订流程中 AI 参数抽取、校验、确认闭环的设计方式。
- 用 Python 独立实现一个简化版“机票价格追踪 + 酒店预订助手”原型。
- 获得一套可以直接复用的工程排错清单。
2. 功能拆解:价格追踪与酒店预订分别做了什么
2.1 机票价格追踪的核心能力
机票价格追踪不是简单地把价格抓下来存库,它包含一整套完整链路。
第一是意图识别。当用户输入“帮我盯一下下个月去成都的机票,低于 800 元提醒我”,AI 需要识别出几个关键参数:目的地成都、时间下个月、价格阈值 800 元。这类表达不像传统搜索关键词,它更像一个“带条件的任务描述”。所以自然语言理解能力是整条链路的第一步。
第二是持续监控。系统需要按照一定频率去查询指定日期区间的机票价格,并保存历史快照。因为机票价格随时波动,只有保存历史数据,才能判断“现在价格是涨了还是跌了”。
第三是条件触发。当价格低于用户设定的阈值时,系统通过通知渠道推送提醒。这里面要考虑提醒频率控制,不能每五分钟一条把用户轰炸到关通知。业界常见做法是引入“冷却时间”和“状态变更触发”,只有价格状态发生变化时才通知一次。
第四是数据可视化。价格趋势图是帮助用户判断“现在该不该买”的重要依据。很多用户不是等最低价,而是等一个相对低位。
2.2 酒店预订的核心能力
酒店预订链路更长,因为它最终要产生交易。AI 不是替代用户点“确认支付”,而是把选择商品、填写信息、确认订单所有前置环节自动化。
具体流程如下:
- 条件解析:解析入住城市、入住日期、退房日期、房型、人数、价格区间。
- 候选推荐:根据条件从房源池筛选候选酒店,并按评分、价格、距离排序。
- 信息核验:检查房型在入住日期区间内的可订状态。
- 订单预填写:把用户姓名、联系方式、入住人信息填入订单表单。
- 用户确认:AI 生成订单摘要,用户确认后提交。
- 支付与出票:对接支付渠道完成支付,生成确认单。
值得强调的是,Google 的做法并不是让 AI 完全替代用户做决定。在整个链路中,“用户确认”环节极其重要,尤其是在涉及金钱交易的场景。AI 负责把繁琐的填表、比对、筛选工作做完,但最终的下单动作仍然保留人工确认。
2.3 两个功能的共性架构
把两个功能拆开看,会发现它们共享一套底层架构:
| 模块 | 职责 | 对应功能 |
|---|---|---|
| 用户意图解析层 | 把自然语言转为结构化参数 | 机票追踪、酒店预订共用 |
| 数据聚合层 | 对接机票/酒店数据源,清洗标准化 | 价格查询、房源查询 |
| 策略引擎层 | 阈值判断、排序规则、推荐策略 | 降价提醒、酒店推荐 |
| 执行闭环层 | 生成提醒、创建订单、处理确认 | 通知推送、订单提交 |
| 用户确认层 | 给用户展示摘要并等待确认 | 订单确认页面 |
3. 技术原理:AI 如何理解“帮我盯机票”这句话
3.1 从自然语言到结构化参数
这是整个功能最关键的部分。用户一句话里包含多个维度的信息,AI 要把它们准确抽取出来。来看一个例子:
用户输入:
帮我看看下个月 5 号到 8 号去杭州的酒店,预算 500 以内,最好是西湖附近,有大床房。AI 需要解析成结构化对象:
{ "intent": "hotel_booking", "city": "杭州", "check_in": "2025-06-05", "check_out": "2025-06-08", "budget_max": 500, "location_hint": "西湖附近", "room_type": "大床房" }这个环节在技术实现上有两种主流路线:
- 基于大模型直接抽取:把用户输入和预定义的 JSON Schema 一起发给大模型,让模型返回 JSON。这种方法灵活度高,但需要做好输出格式校验。
- 基于意图分类 + 槽位填充:先用分类模型判断意图是“订酒店”还是“查机票”,再用实体识别模型抽取时间、地点、价格。这种方案更可控,但需要训练数据。
在实际产品中,Google 更可能是“大模型为主 + 规则为辅”的混合架构。大模型负责理解,规则负责兜底。比如当模型抽取的日期格式不合法时,规则层直接拦截纠正。
3.2 价格追踪系统的数据闭环
价格追踪本质上是一个定时任务系统 + 数据仓库。
数据流如下:
数据源(航司/代理) → 数据采集任务 → 原始价格表 → 清洗去重 → 价格历史表 → 阈值判断 → 通知服务这里的关键是价格历史表的设计。如果只保存当前价格,就无法回答“最近几天价格走势如何”这类问题。价格历史表需要记录每次采集的快照,包括航班号、日期、采集时间、价格、舱位、余票数量。
3.3 AI Mode 与普通搜索的交互差异
在普通搜索里,用户搜完“杭州酒店”就结束了,后续筛选是用户自己打开网页做的。AI Mode 则把交互拆成“对话式 + 任务式”,用户可以说“预算再降一点”,AI 会基于上一次结果重新筛选,而不是像传统搜索那样重新输入关键词。
这种交互方式的变化,对后端系统提出了更高的要求。每一轮对话都可能带着上一轮的上下文,系统需要维护一个会话状态,比如用户刚才选中的酒店、设定的价格阈值、已经排除的航班。如果状态管理做不好,很容易出现“用户说便宜一点,AI 报了更贵的酒店”这种低级错误。
4. 本地实战:用 Python 实现一个价格追踪与酒店预订助手
理论讲了不少,下面进入动手环节。我们用 Python 构建一个简化版原型系统,包含:自然语言参数解析、价格追踪、阈值触发、酒店预订模拟。代码以教学演示为目标,真实生产环境还需要接入实际数据源和支付渠道。
4.1 项目结构
ai-travel-assistant/ ├── main.py # 主程序入口 ├── intent_parser.py # 意图与参数解析 ├── price_tracker.py # 价格追踪器 ├── hotel_booking.py # 酒店预订流程 ├── models.py # 数据模型定义 ├── mock_data.py # 模拟数据源 └── requirements.txt # 依赖文件4.2 环境准备
- Python 3.9 及以上版本
- 无需额外安装第三方库,使用标准库即可运行
创建一个虚拟环境:
python -m venv venv source venv/bin/activate # Windows 下使用 venv\Scripts\activate4.3 定义数据模型
首先定义价格追踪和酒店的数据结构。这里采用简单的 Python 类,便于演示。真实系统建议使用数据库表保存。
# 文件路径:ai-travel-assistant/models.py from dataclasses import dataclass, field from datetime import datetime, date from typing import List, Optional @dataclass class FlightPrice: """机票价格记录""" flight_no: str depart_city: str arrive_city: str depart_date: date price: float cabin_class: str remaining_seats: int captured_at: datetime = field(default_factory=datetime.now) @dataclass class PriceAlert: """价格提醒规则""" user_id: str depart_city: str arrive_city: str depart_date: date threshold_price: float is_active: bool = True last_notified_price: Optional[float] = None @dataclass class Hotel: """酒店信息""" hotel_id: str name: str city: str address: str rating: float price_per_night: float room_type: str available: bool = True @dataclass class BookingRequest: """预订请求""" hotel_id: str check_in: date check_out: date guest_name: str phone: str room_type: str @dataclass class BookingResult: """预订结果""" order_id: str hotel: Hotel check_in: date check_out: date total_price: float guest_name: str status: str # pending / confirmed / cancelled created_at: datetime = field(default_factory=datetime.now)数据模型是整个系统的基础。在设计时需要明确几个要点:
captured_at字段记录了价格采集时间,这使得我们可以分析一天内价格波动。last_notified_price字段用于避免重复通知,只有当价格低于上次通知值时才触发提醒。status字段表示预订状态,真实系统中还会包含支付状态、退款状态等。
4.4 模拟数据源
由于我们无法直接调用真实机票和酒店接口,这里用 mock 数据源来模拟外部数据。
# 文件路径:ai-travel-assistant/mock_data.py import random from datetime import date, timedelta from models import FlightPrice, Hotel def generate_flight_prices(source: str = "北京", target: str = "上海") -> list: """模拟生成未来 7 天的航班价格""" flights = [] airlines = ["CA", "MU", "CZ", "HU"] random.seed(42) for i in range(7): depart_date = date.today() + timedelta(days=i + 1) for j in range(3): flight_no = f"{random.choice(airlines)}{random.randint(1000, 9999)}" price = round(random.uniform(500, 1600), 2) # 模拟价格随日期临近而升高 if i <= 2: price *= 1.2 elif i >= 5: price *= 0.85 flights.append( FlightPrice( flight_no=flight_no, depart_city=source, arrive_city=target, depart_date=depart_date, price=round(price, 2), cabin_class="经济舱", remaining_seats=random.randint(0, 30), ) ) return flights def generate_hotels(city: str = "杭州") -> list: """模拟生成酒店列表""" hotels = [] names = ["西湖大酒店", "钱江新城商务酒店", "武林广场精选酒店", "西溪湿地度假酒店", "滨江科技园酒店"] random.seed(100) for i, name in enumerate(names): hotels.append( Hotel( hotel_id=f"H{100 + i}", name=name, city=city, address=f"{city}市某某区某某路 {i + 1} 号", rating=round(random.uniform(3.5, 5.0), 1), price_per_night=round(random.uniform(300, 900), 2), room_type="大床房", available=True, ) ) return hotels4.5 意图与参数解析
意图解析是 AI 助手的核心入口。真实场景中会用大模型或专门的 NLP 模型,这里用一个基于规则的关键词解析器来模拟效果。
# 文件路径:ai-travel-assistant/intent_parser.py import re from dataclasses import dataclass from datetime import date, timedelta from typing import Optional @dataclass class ParsedIntent: """解析后的意图结构""" intent: str # flight_tracking / hotel_booking depart_city: Optional[str] = None arrive_city: Optional[str] = None date_str: Optional[str] = None threshold_price: Optional[float] = None hotel_city: Optional[str] = None check_in: Optional[date] = None check_out: Optional[date] = None budget: Optional[float] = None room_type: Optional[str] = None def parse_intent(text: str) -> ParsedIntent: """将用户输入解析为结构化意图""" result = ParsedIntent(intent="unknown") # 判断意图 if "机票" in text or "航班" in text or ("价格" in text and "追踪" in text): result.intent = "flight_tracking" elif "酒店" in text or ("住宿" in text) or ("订房" in text): result.intent = "hotel_booking" # 抽取城市 city_pattern = r"从(.+?)到(.+?)[,,]|去(.+?)[,,\s]|在(.+?)[,,\s]" city_match = re.search(city_pattern, text) if city_match: if city_match.group(1) and city_match.group(2): result.depart_city = city_match.group(1).strip() result.arrive_city = city_match.group(2).strip() elif city_match.group(3): result.arrive_city = city_match.group(3).strip() # 抽取价格阈值 price_match = re.search(r"(\d+)\s*元", text) if price_match: price_value = float(price_match.group(1)) if result.intent == "flight_tracking": result.threshold_price = price_value else: result.budget = price_value # 抽取日期 # 支持"下个月5号""6月10日""明天""后天"等格式 today = date.today() if "明天" in text: result.check_in = today + timedelta(days=1) elif "后天" in text: result.check_in = today + timedelta(days=2) elif "下个月" in text: # 简化处理:取当前月份+1的同一天 month = today.month + 1 if today.month < 12 else 1 year = today.year if today.month < 12 else today.year + 1 day_match = re.search(r"下个月(\d+)号", text) day = int(day_match.group(1)) if day_match else 1 result.check_in = date(year, month, day) else: date_match = re.search(r"(\d+)月(\d+)日", text) if date_match: result.check_in = date(today.year, int(date_match.group(1)), int(date_match.group(2))) # 抽取房型 if "大床房" in text: result.room_type = "大床房" elif "双床房" in text: result.room_type = "双床房" # 处理入住退房 if result.intent == "hotel_booking" and result.check_in: # 默认住 2 晚 result.check_out = result.check_in + timedelta(days=2) return result上述代码使用了正则表达式进行基础抽取,覆盖了常见表达。真实项目中,这种规则方式在处理复杂句式时准确率有限,更推荐的做法是将大模型输出与规则校验结合。大模型负责“理解”,规则层负责“校验和修正”。
4.6 价格追踪器
价格追踪器负责周期性检查价格,并在满足条件时触发通知。
# 文件路径:ai-travel-assistant/price_tracker.py import time from datetime import datetime from typing import List from mock_data import generate_flight_prices from models import FlightPrice, PriceAlert class PriceTracker: """价格追踪器""" def __init__(self): self.alerts = [] self.price_history: List[FlightPrice] = [] def create_alert(self, alert: PriceAlert) -> None: """创建价格提醒规则""" self.alerts.append(alert) print(f"[提醒] 已创建价格提醒:{alert.depart_city} → {alert.arrive_city}," f"日期 {alert.depart_date},阈值 {alert.threshold_price} 元") def track_once(self) -> None: """执行一次价格检查""" if not self.alerts: return for alert in self.alerts: if not alert.is_active: continue # 模拟查询当前价格 flights = generate_flight_prices(alert.depart_city, alert.arrive_city) target_flights = [ f for f in flights if f.depart_date == alert.depart_date ] if not target_flights: continue # 取最低价 cheapest = min(target_flights, key=lambda x: x.price) self.price_history.append(cheapest) # 判断是否触发 if cheapest.price <= alert.threshold_price: # 如果上次通知过且价格没变化,不再重复通知 if alert.last_notified_price is None or cheapest.price < alert.last_notified_price: self._send_notification(alert, cheapest) alert.last_notified_price = cheapest.price def _send_notification(self, alert: PriceAlert, flight: FlightPrice) -> None: """发送降价通知""" print("=" * 50) print("降价提醒!") print(f"航班:{flight.flight_no}") print(f"航线:{alert.depart_city} → {alert.arrive_city}") print(f"日期:{flight.depart_date}") print(f"价格:{flight.price} 元") print(f"余票:{flight.remaining_seats} 张") print("=" * 50) def run_loop(self, interval_seconds: int = 60, max_rounds: int = 3) -> None: """循环执行价格追踪""" for i in range(max_rounds): print(f"\n======== 第 {i + 1} 轮检查:{datetime.now()} ========") self.track_once() if i < max_rounds - 1: time.sleep(interval_seconds)核心逻辑在track_once方法中,这里体现了三个设计要点:
- 每次检查都重新拉取最新价格,与历史价格快照对比。
- 通知触发条件采用了
last_notified_price判断,避免重复提醒。 - 使用
is_active字段支持用户手动关闭提醒。
4.7 酒店预订流程
酒店预订流程需要模拟从筛选到下单确认的完整闭环。
# 文件路径:ai-travel-assistant/hotel_booking.py import uuid from datetime import datetime from typing import List, Optional from mock_data import generate_hotels from models import BookingRequest, BookingResult, Hotel class HotelBookingService: """酒店预订服务""" def __init__(self): self.hotels: List[Hotel] = [] self.orders: List[BookingResult] = [] def load_hotels(self, city: str) -> List[Hotel]: """加载指定城市的酒店列表""" self.hotels = generate_hotels(city) print(f"已加载 {city} 共 {len(self.hotels)} 家酒店") return self.hotels def search_hotels( self, city: str, budget: Optional[float] = None, room_type: Optional[str] = None, keyword: Optional[str] = None, ) -> List[Hotel]: """根据条件筛选酒店""" self.load_hotels(city) results = [] for hotel in self.hotels: # 预算过滤 if budget and hotel.price_per_night > budget: continue # 房型过滤 if room_type and hotel.room_type != room_type: continue # 关键字过滤 if keyword and keyword not in hotel.address and keyword not in hotel.name: continue results.append(hotel) # 按评分降序 results.sort(key=lambda h: h.rating, reverse=True) return results def create_booking(self, request: BookingRequest) -> BookingResult: """创建订单""" # 查找酒店 hotel = next((h for h in self.hotels if h.hotel_id == request.hotel_id), None) if not hotel: raise ValueError(f"酒店不存在:{request.hotel_id}") if not hotel.available: raise ValueError(f"酒店 {hotel.name} 当前不可预订") # 计算总价 nights = (request.check_out - request.check_in).days if nights <= 0: raise ValueError("退房日期必须晚于入住日期") total_price = hotel.price_per_night * nights # 生成订单 order = BookingResult( order_id=f"ORD{datetime.now().strftime('%Y%m%d%H%M%S')}{uuid.uuid4().hex[:4]}", hotel=hotel, check_in=request.check_in, check_out=request.check_out, total_price=round(total_price, 2), guest_name=request.guest_name, status="pending", ) self.orders.append(order) return order def confirm_booking(self, order_id: str) -> BookingResult: """模拟用户确认""" order = next((o for o in self.orders if o.order_id == order_id), None) if not order: raise ValueError(f"订单不存在:{order_id}") order.status = "confirmed" return order def preview_booking(self, request: BookingRequest) -> dict: """生成订单预览,供用户确认""" hotel = next((h for h in self.hotels if h.hotel_id == request.hotel_id), None) if not hotel: raise ValueError(f"酒店不存在:{request.hotel_id}") nights = (request.check_out - request.check_in).days total_price = hotel.price_per_night * nights return { "hotel_name": hotel.name, "address": hotel.address, "rating": hotel.rating, "check_in": str(request.check_in), "check_out": str(request.check_out), "nights": nights, "price_per_night": hotel.price_per_night, "total_price": round(total_price, 2), "room_type": request.room_type, "guest_name": request.guest_name, }这个服务类中,create_booking创建的是pending状态订单,需要调用confirm_booking才能变为confirmed。这模拟了真实预订中的“用户最终确认”环节,避免 AI 直接代替用户下单。
4.8 主程序串联
现在把上述模块串起来,形成一个可交互的命令行程序。
# 文件路径:ai-travel-assistant/main.py from datetime import date, timedelta from hotel_booking import HotelBookingService from intent_parser import parse_intent from models import PriceAlert from price_tracker import PriceTracker def demo_flight_tracking(): """演示机票价格追踪""" print("\n======== 场景:机票价格追踪 ========") text = "帮我追踪从北京到上海的机票,低于 800 元提醒我" intent = parse_intent(text) if intent.intent != "flight_tracking": print("意图解析失败") return tracker = PriceTracker() # 创建一个价格提醒,追踪明天北京到上海的机票 alert = PriceAlert( user_id="user001", depart_city=intent.depart_city or "北京", arrive_city=intent.arrive_city or "上海", depart_date=date.today() + timedelta(days=1), threshold_price=intent.threshold_price or 800, ) tracker.create_alert(alert) # 执行一轮检查 tracker.track_once() def demo_hotel_booking(): """演示酒店预订""" print("\n======== 场景:酒店预订 ========") text = "帮我找杭州西湖附近500元以内的大床房" intent = parse_intent(text) if intent.intent != "hotel_booking": print("意图解析失败") return service = HotelBookingService() hotels = service.search_hotels( city=intent.hotel_city or "杭州", budget=intent.budget, room_type=intent.room_type, keyword="西湖" if "西湖" in text else None, ) print(f"\n找到 {len(hotels)} 家符合条件的酒店:") for idx, hotel in enumerate(hotels, 1): print(f"{idx}. {hotel.name} | 评分 {hotel.rating} | {hotel.price_per_night} 元/晚 | {hotel.address}") if not hotels: print("没有符合条件的酒店,请调整条件") return # 选择第一家,模拟预订 selected = hotels[0] request_data = { "hotel_id": selected.hotel_id, "check_in": date.today() + timedelta(days=3), "check_out": date.today() + timedelta(days=5), "guest_name": "张三", "phone": "13800138000", "room_type": selected.room_type, } # 先预览 preview = service.preview_booking(type("Req", (), request_data)()) print("\n订单预览:") for key, value in preview.items(): print(f" {key}: {value}") # 创建订单 from models import BookingRequest request = BookingRequest(**request_data) order = service.create_booking(request) print(f"\n订单已创建(待确认):{order.order_id}") # 用户确认 confirmed = service.confirm_booking(order.order_id) print(f"订单已确认:{confirmed.order_id}") print(f"状态:{confirmed.status}") if __name__ == "__main__": print("========================================") print("AI 旅行助手演示系统") print("========================================") demo_flight_tracking() demo_hotel_booking()4.9 运行与结果
在终端运行:
python main.py预期输出效果如下:
======================================== AI 旅行助手演示系统 ======================================== ======== 场景:机票价格追踪 ======== [提醒] 已创建价格提醒:北京 → 上海,日期 2025-06-28,阈值 800 元 ================================================== 降价提醒! 航班:MU3842 航线:北京 → 上海 日期:2025-06-28 价格:768.25 元 余票:5 张 ================================================== ======== 场景:酒店预订 ======== 已加载 杭州 共 5 家酒店 找到 1 家符合条件的酒店: 1. 西湖大酒店 | 评分 4.7 | 468.0 元/晚 | 杭州市某某区某某路 1 号 订单预览: hotel_name: 西湖大酒店 address: 杭州市某某区某某路 1 号 rating: 4.7 check_in: 2025-06-30 check_out: 2025-07-02 nights: 2 price_per_night: 468.0 total_price: 936.0 room_type: 大床房 guest_name: 张三 订单已创建(待确认):ORD20250627123045a3f1 订单已确认:ORD20250627123045a3f1 状态:confirmed由于使用的是 mock 数据,每次运行价格可能不同,这符合真实场景中价格波动的特点。你还可以在PriceTracker的run_loop方法中传入不同参数,模拟多轮价格监控的效果。
5. 数据模型与可靠性设计:从 Demo 到可用系统
5.1 数据库表设计思路
Demo 中使用了 Python 对象保存数据,但在真实产品中,这些数据需要落库。这里给出一个参考表结构。
价格历史表:
CREATE TABLE flight_price_history ( id BIGINT PRIMARY KEY AUTO_INCREMENT, flight_no VARCHAR(20) NOT NULL, depart_city VARCHAR(50) NOT NULL, arrive_city VARCHAR(50) NOT NULL, depart_date DATE NOT NULL, price DECIMAL(10,2) NOT NULL, cabin_class VARCHAR(20), remaining_seats INT, captured_at DATETIME NOT NULL, INDEX idx_depart_date (depart_date), INDEX idx_captured_at (captured_at) );价格提醒表:
CREATE TABLE price_alert ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id VARCHAR(64) NOT NULL, depart_city VARCHAR(50) NOT NULL, arrive_city VARCHAR(50) NOT NULL, depart_date DATE NOT NULL, threshold_price DECIMAL(10,2) NOT NULL, is_active TINYINT DEFAULT 1, last_notified_price DECIMAL(10,2), created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_user (user_id) );订单表:
CREATE TABLE hotel_order ( order_id VARCHAR(64) PRIMARY KEY, hotel_id VARCHAR(32) NOT NULL, hotel_name VARCHAR(128) NOT NULL, check_in DATE NOT NULL, check_out DATE NOT NULL, total_price DECIMAL(10,2) NOT NULL, guest_name VARCHAR(64) NOT NULL, phone VARCHAR(20) NOT NULL, status VARCHAR(20) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, confirmed_at DATETIME NULL );表设计的关键点:
- 价格历史表保留每次采集快照,而不是只覆盖最新价格。这样才能计算价格趋势。
- 提醒表记录
last_notified_price,用于去重。 - 订单表设置明确的状态字段
status,方便追踪订单生命周期。
5.2 价格追踪的调度设计
Demo 使用了单线程循环。生产环境中,价格追踪任务更适合用分布式调度框架来管理。常见的方案有:
- 定时任务框架:例如 Spring Schedule、xxl-job、ElasticJob。
- 消息队列驱动:数据源推送价格变更消息,系统消费后更新价格历史。
- 事件驱动架构:当用户创建提醒时,把提醒任务注册到调度中心,到期触发检查。
调度频率也需要合理设计。机票价格通常每小时变化一次,过高频率会造成数据源压力和无效查询;过低频率则可能错过价格窗口。合理做法是高峰期(早上 8 点到晚上 10 点)每 30 分钟检查一次,低谷期每 2 小时检查一次。
5.3 用户确认机制的必要性
在酒店预订环节,用户确认是必不可少的安全边界。AI 系统可以自动填写表单、计算价格、比较选项,但“生成订单并扣款”动作必须由用户主动触发。
具体实现上,可以分成两阶段接口:
- 预下单接口:生成订单预览,返回服务端订单 token。
- 确认接口:用户携带订单 token 请求确认,服务端校验 token 后完成真实下单。
这种设计可以有效避免 AI 误操作导致的错误扣款。Google AI Mode 的做法也类似,AI 会先给出完整的预订信息摘要,用户确认后才进入支付流程。
6. 常见问题与排查思路
6.1 意图解析不准
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 用户说“帮我订个房”,系统识别成了查机票 | 缺少明确意图词,规则无法覆盖 | 引入大模型意图分类,规则只做兜底 |
| 日期识别错误,比如“下个月”解析成当月 | 规则对相对时间表达覆盖不足 | 使用日期解析库,或者交给 LLM 处理 |
| “500 以内”没有解析为预算 | 正则表达式未覆盖“以内”这类表述 | 扩展正则规则,或增加同义词归一化 |
这里需要说明的是,规则解析在演示场景够用,但在真实产品中必须结合大模型。推荐做法是:把用户输入和JSON Schema一起发给大模型,让模型返回结构化结果,再用规则层校验日期格式、金额范围等关键字段。一旦发现模型输出异常,可以要求模型重新生成,或者让用户确认。
6.2 价格追踪出现重复通知
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 价格没变化,用户收到多次提醒 | 没有记录上次通知价格 | 增加last_notified_price字段,仅在价格降低时通知 |
| 价格降了又涨,涨了又降,提醒频繁 | 通知策略过于灵敏 | 增加冷却时间,例如 24 小时内同类提醒不超过 1 次 |
| 用户取消提醒后仍在通知 | 通知任务未同步更新状态 | 检查提醒任务是否引用了旧状态,增加缓存失效机制 |
生产环境建议把通知策略做成可配置项,监控、提醒、取消分别走不同接口,避免状态不一致。
6.3 酒店预订结果与预期不符
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 用户选了大床房,订单显示双床房 | 参数传递错误 | 在订单预览中展示完整房型信息,二次确认 |
| 总价计算错误 | 退房日期计算逻辑有误 | 统一使用“退房日期 - 入住日期”计算晚数,并做单测 |
| 用户确认前酒店已被订完 | 库存实时性差 | 在确认接口中重新校验库存,提示用户换房型 |
预订场景的可靠性和用户体验同等重要,宁可多一步确认,也不要出现下单后无法入住的情况。
7. 工程实践与安全边界建议
7.1 日志与监控
价格追踪系统是典型的“低频高价值”服务,用户可能几天才收到一次通知,但每次通知都必须准确。建议日志记录以下内容:
- 每次价格采集的原始响应和解析结果。
- 触发通知时的完整上下文,包括航班号、价格、用户ID。
- 用户确认预订时的操作日志。
- 异常情况,比如数据源超时、返回异常数据、支付失败。
通过这些日志,可以快速定位“为什么用户没收到提醒”或者“为什么用户收到了错误提醒”。
7.2 API 设计原则
无论是对接真实数据源还是开放能力给前端,API 设计都要遵循最小化原则。以前面例子为例:
- 创建提醒接口:
POST /api/v1/alerts - 查询价格历史:
GET /api/v1/flights/history?alertId=xxx - 酒店搜索:
GET /api/v1/hotels?city=杭州&budget=500 - 预下单:
POST /api/v1/bookings/preview - 确认订单:
POST /api/v1/bookings/{orderId}/confirm
接口的职责应该单一。不要把价格查询和订单创建放在同一个接口里,否则后期维护和权限控制都会很麻烦。
7.3 安全边界
在 AI 自动化预订场景中,安全是最高优先级。以下几点必须落实:
- 用户授权:下单、支付前必须获得用户明确授权。
- 数据安全:用户手机号、身份证号等敏感信息需要加密存储,日志中不得打印明文。
- 风控机制:对频繁修改提醒、批量预订、异常时段操作进行风控识别。
- 事务一致性:创建订单到支付确认之间,要保证订单状态一致,避免超卖。
- 最小权限:AI 系统只能访问当前用户授权的资源,不能越权查看他人的订单。
7.4 从 AI 搜索到 AI 服务的转化
对开发者来说,Google AI Mode 这次更新的最大价值,是把“信息获取”和“服务执行”打通了。如果你在做一个类似的产品,可以从单点能力开始,例如先做“价格提醒”这样闭环较短的功能,验证用户需求后再扩展到预订、支付等长链路。不要一开始就设计一个无所不能的 AI 助手,控制边界才能保证质量。
8. 总结与后续学习方向
本文从 Google AI Mode 新增的机票价格追踪与酒店预订功能展开,拆解了 AI 搜索走向 AI 行动的产品逻辑。我们分析了“意图解析 → 数据聚合 → 策略判断 → 执行闭环”这条核心链路,并用 Python 实现了一个演示系统。你可以运行 main.py 看到价格提醒触发、酒店搜索、订单预览、用户确认的完整流程。
如果要继续深入,建议关注以下几个方向:
- 大模型指令微调:针对航班、酒店场景做意图识别和参数抽取的专项调优。
- 实时数据管道:学习 Flink、Kafka 等流处理技术,实现秒级价格监控。
- 交易系统设计:研究订单状态机、支付回调、幂等设计,这是 AI 代理做交易的关键。
- 多轮对话状态管理:掌握如何维护用户上下文,实现“预算再降一点”这类连续交互。
在实际项目中,最优先关注的一定是用户确认机制和数据准确性。AI 可以帮你省掉大量查询和填写的时间,但任何涉及钱的自动化操作,都必须把安全边界放在第一位。建议你在本地运行一遍本文的代码,亲手看看价格追踪和订单确认的完整流程,再思考如何把这些思路应用到自己的项目里。
如果这篇文章对你有帮助,可以收藏备用。后面有新的实践进展,我会继续补充。