1. 项目概述:一个为智能体打造的旅行工具箱
如果你也像我一样,经常需要为家人或自己规划旅行,那你一定知道这活儿有多琐碎。查汇率、看天气、找酒店、比机票、做攻略……每个环节都得打开不同的网站或App,信息散落各处,光是整理就要花上大半天。更别提那些临行前的焦虑了:签证材料齐了吗?航班会不会延误?目的地的餐厅要不要提前订?
最近我在折腾一个叫OpenClaw的AI智能体开发框架,就想,能不能让AI来帮我搞定这些烦心事?于是就有了ClawTourism这个项目。它本质上是一个全栈旅行智能工具箱,但它的特别之处在于,它是专门为OpenClaw这样的AI智能体(Agent)设计的“技能包”。你可以把它理解为一个超级助理的“大脑插件”,装上它,你的AI助理就瞬间精通了从行前规划到途中协助的所有旅行事务。
这个工具箱最吸引我的有两点:一是模块化,二是零配置友好。它把旅行中需要的各种能力拆成了一个个独立的模块,比如currency(货币)、destination(目的地情报)、weather(天气)。更棒的是,其中很多核心模块,像汇率查询、国家百科、天气预报,完全不需要你申请任何API密钥,开箱即用。这对于想快速搭建一个旅行助手原型,或者不想被各种API申请流程劝退的开发者来说,简直是福音。
它适合谁呢?首先是像我一样的OpenClaw开发者或爱好者,你可以直接把它作为技能集成到你的智能体中,赋予它强大的旅行规划能力。其次,任何对Python自动化、旅行科技感兴趣的开发者,都可以把它当作一个极佳的学习案例,看看如何将多个异构的数据源(银行汇率、维基百科、航班雷达等)整合成一个连贯的服务。最后,即便是普通的旅行爱好者,如果你不介意在命令行里敲敲打打,也能用它来高效地获取一站式的旅行信息。
2. 核心模块深度解析与设计思路
ClawTourism的威力来自于其精心设计的模块化架构。每个模块都专注于解决旅行中的一个具体痛点,并且根据数据源的特性,分成了“免密钥”和“需密钥”两大类。这种设计背后有很实际的考量。
2.1 免密钥模块:数据开放的魅力
免密钥模块是项目的基石,它们依赖的都是免费、开放的数据源。这不仅仅是为了降低使用门槛,更体现了一种构建可持续服务的设计哲学——尽可能减少对外部商业API的强依赖。
currency(货币转换):这个模块使用了Frankfurter API。它不是一个商业产品,而是一个由欧洲中央银行等官方数据驱动的开源项目。这意味着它的汇率数据相对权威、稳定,并且没有调用次数限制(在合理范围内)。在实现上,模块不仅提供了实时转换(convert),还支持查看某一货币的所有汇率(rates)以及查询历史汇率(historical)。对于旅行规划,查看历史汇率波动趋势,有时比只看当下汇率更有参考价值。
destination(目的地情报):这是我个人最喜欢的一个模块,它巧妙地将两个数据源融合。RestCountries API提供了结构化的国家基本信息,如首都、货币、语言、时区,这些是规划行程的元数据。而Wikivoyage(维基导游)则提供了鲜活的、由旅行者贡献的攻略内容,包括“景点”、“餐饮”、“住宿”、“交通”等板块。模块的设计是,先通过country子命令快速获取国家事实,再通过guide子命令深入获取某个城市的详细攻略。这种“总-分”结构的信息获取方式,非常符合人类规划旅行的思维习惯。
weather(天气预报):基于Open-Meteo,这是一个提供免费天气API的服务,数据来源于全球气象模型。它提供了7-14天的预报,对于行前准备完全足够。模块设计的关键在于参数化,比如可以指定预报天数(--days),未来可以轻松扩展为按小时预报或获取历史天气。
visa_check(签证检查):这是一个特化模块,目前主要针对以色列护照持有人,内置了35+个国家的入境要求查询表。它的实现方式很直接——一个本地的查找表(Lookup Table)。这种设计虽然看起来“笨”,但对于规则明确、变化不频繁的数据(如免签、落地签、需提前申请签证),反而是最可靠、响应最快的方式。当然,它的可扩展性在于这个查找表可以不断维护和更新。
flight-status与flight-monitor(航班状态与监控):这两个模块基于对FlightRadar24数据的非官方访问。flight-status提供单次查询,而flight-monitor则是一个“有状态”的监控器。后者的设计非常巧妙:它会将上一次查询的航班状态(如延误时间、登机口)保存到一个指定的状态文件(--state-file)中。下次运行时,只对比当前状态与保存状态的差异,仅在发生变化(如延误超过15分钟、登机口变更)时才输出结果。这天生就是为了配合cron定时任务设计的,可以实现“静默监控,异常告警”,避免信息轰炸。
实操心得:选择开放数据源在构建这类工具时,优先考虑像Frankfurter、RestCountries、Open-Meteo、Wikivoyage这样的开放数据源。它们通常没有商业化的压力,数据质量有保障,且长期稳定性更好。虽然可能需要自己处理一下数据格式,但换来的零成本和零依赖是非常值得的。
2.2 需密钥模块:与商业服务的集成
当开放数据无法满足需求时,就需要集成商业API。ClawTourism在这方面选择了几个在旅行领域具有代表性的服务。
places(地点搜索):集成Google Places API。选择它的原因很直接:POI(兴趣点)数据最全、最准,而且支持丰富的筛选条件(如类型、评分、是否适合家庭)。它的免费额度(每月5千次请求)对于个人或轻量级应用来说也足够。模块设计了restaurants、attractions和通用search子命令,特别是--family参数,能自动过滤出动物园、水族馆等适合家庭的景点,这个小细节非常贴心。
accommodation(住宿搜索)与flights(航班搜索):都通过RapidAPI平台接入Booking.com的API。选择RapidAPI作为中间层,而非直接对接,简化了认证和请求流程。这两个模块的参数设计体现了对真实旅行需求的深刻理解:住宿搜索支持按城市、日期、入住人数、儿童年龄(而不仅仅是数量)、最低评分进行筛选;航班搜索同样支持多乘客类型。这些参数能组合出非常精准的查询。
airbnb(民宿搜索):通过Apify平台的爬虫工具来获取数据。这是一个很务实的方案。由于Airbnb没有公开的官方API,使用可靠的第三方爬虫服务是获取其房源数据的可行方法。这提醒我们,在开发生态中,并非所有数据都能通过“标准接口”获得,有时需要利用一些“非标准”但合规的工具。
| 模块 | 核心数据源 | 关键特性 | 适用场景 |
|---|---|---|---|
| currency | Frankfurter API | 实时/历史汇率,零API密钥 | 行前预算规划,消费换算 |
| destination | RestCountries + Wikivoyage | 国家事实+深度旅行指南 | 目的地初步调研与深度了解 |
| weather | Open-Meteo | 7-14天预报 | 决定行李衣物,安排户外活动 |
| visa_check | 内置查找表 | 快速签证要求查询 | 检查出入境资格 |
| flight-status | FlightRadar24 | 实时航班动态 | 出发当天查看航班情况 |
| places | Google Places API | 餐厅、景点、POI搜索 | 规划每日游玩和餐饮 |
| accommodation | Booking.com (RapidAPI) | 酒店搜索与比价 | 预订酒店住宿 |
| airbnb | Apify 爬虫 | 民宿/公寓搜索 | 寻找更具生活感的住宿 |
| flights | Booking.com (RapidAPI) | 航班价格与时刻查询 | 比对和预订机票 |
3. 从邮件到行程:自动化流水线实战
ClawTourism不只是一个被动的查询工具,它的精髓在于那条自动化的预出行流水线(Pre-Trip Pipeline)。这条流水线的起点,往往是我们邮箱里那一堆混乱的预订确认邮件。
3.1 行程扫描与智能提取
项目通过scanner.py、extractor.py和pdf_extractor.py等模块,构建了一个邮件处理引擎。它的工作流程是这样的:
- 扫描(Scan):
scanner.py模块会定期扫描Gmail中特定标签(如Trips)下的邮件。这里依赖另一个名为gog的技能来处理Gmail认证,避免了在ClawTourism中直接处理复杂的OAuth流程,体现了模块化设计的思想。 - 提取(Extract):
extractor.py是核心的“大脑”。它使用正则表达式和自然语言处理(NLP)的一些基础技巧,从邮件正文的纯文本中识别关键信息:航班号、起降城市、日期时间、酒店名称、入住离店日期、预订码(PNR)等。这是一个典型的信息抽取(Information Extraction)任务,精度直接决定了后续流程的可靠性。 - PDF解析:很多确认单,尤其是机票和租车订单,是以PDF附件形式存在的。
pdf_extractor.py模块负责解析这些PDF,提取文字信息,再交给提取器处理。这里可能会用到像PyPDF2或pdfplumber这样的库。 - 组装与持久化:
assembler.py将散落在多封邮件中的信息,按照“旅行”的概念进行聚合。例如,它会把同一趟行程的去程航班、返程航班、酒店预订等信息,归类到一个Trip对象中。最后,store.py将这个结构化的行程对象,以JSON格式保存到本地文件系统(如memory/trips/{行程标识}.json),完成持久化。
# 启动一次全量扫描 ct scan # 输出:Found 3 new trip emails. Assembled into trip 'paris-2025-spring'.这个过程完全自动化,理想状态下,你只需要把预订邮件拖进Trips标签,剩下的就交给ClawTourism了。
3.2 缺口检测与智能提醒
行程信息被结构化存储后,gap_detector.py模块就开始发挥作用了。它会像一个细心的管家一样,检查你的行程是否存在“缺口”:
- 缺失返程航班:只有去程,没有返程?
- 住宿空缺:在某个日期段内,没有对应的酒店或民宿预订?
- 关键文档缺失:行程中涉及签证,但系统里没有找到签证文件?
一旦检测到缺口,它就会生成提醒。这比单纯罗列信息更进一步,提供了主动的、基于上下文的风险提示。
3.3 行前简报流水线:时间就是一切
这是整个项目自动化皇冠上的明珠。pre_trip.py和briefing.py等模块共同定义并执行了一套基于时间的触发规则。
它不再是“你现在问我答”,而是“在正确的时间,主动给你最需要的信息”。这套流水线模拟了一个专业旅行顾问的节奏:
| 时间节点 | 触发动作 | 设计逻辑与实现细节 |
|---|---|---|
| 行程保存时 | 自动检查所有目的地的签证要求。 | 在assembler.py保存行程后立即调用visa_check模块。实现上是为每个目的地国家并行发起查询,汇总结果。 |
| 出发前14天 (D-14) | 检查护照有效期、旅行保险、邮轮在线值机(如适用),并复核文档缺口。 | briefing.py中配置cron任务,在特定日期触发。护照检查通过计算有效期与行程结束日期的差值实现。 |
| 出发前7天 (D-7) | 1. 全栈集成测试:对目的地执行一次完整的模拟查询(货币、攻略、住宿、航班等),确保所有服务可用。 2. 生成打包清单、汇率参考、目的地指南摘要。 | 这是最复杂的一步。pre_trip.py会调用一个专门的测试脚本(test-live-thorough.sh),用真实参数(你的目的地、日期)跑一遍所有模块。确保服务稳定后,再调用packing.py(结合天气和行程类型生成清单)和destination模块生成摘要。 |
| 出发前3天 (D-3) | 提供7天天气预报和行前物流清单(如打印文件、充电器、告知邻居等)。 | 调用weather模块,并从一个预定义的物流清单模板中读取内容。 |
| 出发前1天 (D-1) | 推送航班详细信息、登机口(如果已分配)、机场交通建议。启动航班状态监控。 | 从存储的行程中读取航班号,调用flight-status,并可能集成transfers.py模块提供交通建议。同时,设置flight-monitor的定时任务。 |
| 出发当天 | 每45分钟检查一次航班状态,仅当发生登机口变更、延误>15分钟、开始登机、取消时推送警报。 | 通过flight_monitor.py和系统的cron调度器实现。--state-file参数保证了只有状态变化才告警,避免骚扰。 |
注意事项:自动化中的“柔性”设计这套流水线看似刚性,但好的实现必须包含“柔性”。例如,
D-7的集成测试如果失败,不应该阻塞后续的打包清单生成,而是应该记录错误并发送警报,让用户知道“某服务暂时不可用,但其他准备可照常进行”。briefing.py模块需要具备良好的错误处理和降级能力。
4. 部署、集成与问题排查实录
4.1 环境配置与API密钥管理
ClawTourism采用了一种轻量级部署方式:它不是通过pip install安装的包,而是通过设置PYTHONPATH直接运行源码。这在技能开发阶段非常方便调试和修改。
# 假设你的技能目录如下 SKILLS=/path/to/your/openclaw/workspace/skills # 将ClawTourism克隆到skills目录下 cd $SKILLS git clone https://github.com/yhyatt/ClawTourism.git clawtourism # 设置一个方便的别名 alias ct="PYTHONPATH=$SKILLS/clawtourism python3 -m clawtourism"对于需要API密钥的模块,项目采用了灵活的管理策略:
- Google Places API:密钥放在
~/.openclaw/google-places-key.txt文件中。这是一种简单的文件式认证。 - RapidAPI Key:支持环境变量(
RAPIDAPI_KEY)或系统的密钥环(keyring)管理。推荐使用keyring,更安全。# 使用keyring存储(以macOS/linux的keyring为例,需先安装`keyring`库) python3 -c "import keyring; keyring.set_password('rapidapi', 'clawtourism', 'your_actual_api_key_here')" - Apify Token:同样使用系统的密钥环管理。
- Gmail认证:依赖独立的
gog技能处理,遵循其自身的认证流程(通常是OAuth)。
这种混合管理方式(文件、环境变量、密钥环)兼顾了简便性和安全性,也体现了与OpenClaw生态其他组件的集成。
4.2 与OpenClaw智能体的集成
ClawTourism作为OpenClaw的一个技能(Skill),其终极价值是被智能体调用。集成后,你的智能体就能理解这样的自然语言指令:
- “帮我查一下下周维也纳的天气。”
- “我们巴黎行程的酒店订好了吗?看看有没有缺口。”
- “出发去东京前一周,提醒我需要准备什么。”
集成方法通常是在你的智能体主程序中,导入ClawTourism的模块,并将其功能注册为智能体可以调用的“工具”(Tools)或“技能”(Skills)。具体实现取决于OpenClaw框架的版本和设计,但核心思想是暴露模块的CLI接口或函数接口给智能体。
4.3 常见问题与排查技巧
在实际部署和运行中,你可能会遇到以下问题:
1. 模块执行报错ModuleNotFoundError: No module named 'clawtourism'
- 原因:
PYTHONPATH没有正确设置,或者当前Python环境缺少依赖包。 - 解决:
- 确认
PYTHONPATH指向了包含clawtourism目录的路径(即/path/to/skills,而不是/path/to/skills/clawtourism)。 - 在
clawtourism目录下,运行pip install -r requirements.txt安装所有依赖。
- 确认
2. API请求失败,返回认证错误或额度不足
- 原因:API密钥未设置、设置错误或已超过免费额度。
- 解决:
- 检查密钥文件:确认
~/.openclaw/google-places-key.txt文件存在且内容正确。 - 检查环境变量/密钥环:运行
python3 -c “import os; print(os.getenv(‘RAPIDAPI_KEY’))”或python3 -c “import keyring; print(keyring.get_password(‘rapidapi’, ‘clawtourism’))”来验证密钥是否正确读取。 - 访问API提供商控制台:如Google Cloud Console、RapidAPI Dashboard,检查该密钥是否启用、额度使用情况。
- 检查密钥文件:确认
3. 航班状态查询返回No flight found
- 原因:FlightRadar24的非官方接口不稳定或航班号格式不正确。
- 解决:
- 确认航班号正确无误(如
CA981)。 - 尝试在FlightRadar24网站或App上直接搜索该航班号,确认航班存在且数据可用。
- 该模块基于非官方接口,存在失效可能。需要关注项目更新,或考虑备用数据源。
- 确认航班号正确无误(如
4.ct scan扫描不到邮件
- 原因:Gmail认证失败,或
gog技能未正确配置,或邮箱中不存在Trips标签。 - 解决:
- 首先确保
gog技能能独立工作,能读取Gmail。 - 在Gmail网页版中,确认存在
Trips标签(区分大小写)。 - 检查
scanner.py中关于标签名的配置是否与你的实际标签名一致。
- 首先确保
5. 行前简报(D-7, D-3等)没有自动触发
- 原因:Cron任务未正确设置,或系统定时任务服务未运行。
- 解决:
- 检查
pre_trip.py中生成的cron指令是否正确。 - 使用
crontab -l查看当前用户的cron任务列表。 - 确保执行cron任务的环境(如
PYTHONPATH)与手动测试时一致。可以在cron命令中显式设置环境变量。 - 查看系统日志(如
/var/log/syslog或journalctl)寻找cron执行错误信息。
- 检查
6. 测试全部通过,但某些模块返回空数据
- 原因:这是最常见的问题之一。可能是查询参数过于严格,或目标地点/时间确实没有数据。
- 排查思路:
- 简化查询:先去掉所有筛选条件(如
--min-rating,--family),只保留必填参数,看是否有结果。 - 检查参数格式:日期是否是
YYYY-MM-DD?城市名是否含有特殊字符?儿童年龄参数是多个数字吗? - 手动验证:用相同的参数,去对应的官方网站(如Booking.com, Google Maps)搜索一下,确认是否有可用数据。
- 查看日志/错误信息:运行命令时添加
-v或--verbose参数(如果支持),或者直接查看模块源代码中的打印语句,看原始API返回了什么。
- 简化查询:先去掉所有筛选条件(如
实操心得:维护一个“运行状态看板”对于这样一个依赖多个外部服务的系统,我习惯创建一个简单的状态检查脚本。这个脚本每天自动运行一次,用最基本的参数调用每个模块(如查一下美元兑欧元汇率、查一下伦敦的天气),不追求结果多精确,只验证服务是否“活着”。一旦某个模块连续失败,就发送通知。这能帮你提前发现API失效、密钥过期等问题,而不是等到真正要用的时候才抓瞎。ClawTourism自带的
scripts/test-live-minimal.sh就是这个思路的完美体现。