用 n8n 串起空间站数据与 AI 明信片
项目用七节点 n8n 工作流获取国际空间站位置,结合卫星影像、逆地理编码和 AI 多语言问候生成可分享明信片。入口通过 PHP 检查限额、生成卡片标识并触发 Webhook。
AI 深度解读
按一下按钮,收到一张来自国际空间站当前位置的明信片:有卫星影像、地名、飞行数据,还有 AI 用你的语言写的问候。作者用 n8n 把它做成了一个可分享的小应用,但更值得拆解的是背后的分工:实时数据负责提供事实,代码负责规则和兜底,模型只负责表达。
这套流程对做 AI 功能的程序员很有参考价值。它把限流、输出检查、图片存储和固定链接都接进了业务链路,同时也留下了几个需要看清的边界:影像并非实时,当地时间也不是手机上的时间。
七个节点里,AI 只负责写几句话
作者在自己的服务器上运行 n8n,整个工作流有七个节点:
Besucher-Knopf (Webhook)接收网站 PHP 转来的请求,携带随机卡片 id 和用户语言。Wo ist die ISS?通过 HTTP GET 请求api.wheretheiss.at/v1/satellites/25544,获取经纬度、高度、速度及空间站处于阳光还是地影中。Was liegt darunter?调用 OpenStreetMap Nominatim 做逆地理编码,也就是把坐标转成地名;zoom level 5取区域和国家,结果使用用户语言。Postkarte schreiben用 Code 节点汇总事实,构建提示词。KI schreibt (IONOS)调用由德国 IONOS 托管的gpt-oss-120b,使用 OpenAI-compatible 接口,返回{greeting, text}JSON。Antwort prüfen检查回答,遇到空值或损坏结果就切换到模板。An die Seite senden携带秘密 token,通过 HTTP POST 把结果送回网站,由 PHP 下载影像、保存卡片和图片,再加入公共展示墙。
空间站高度约 420 km,速度约 27,600 km/h。这些数值由数据接口提供,模型接到的任务只是写两到四句问候。提示词要求语气轻松、数字原样保留,不得编造超出常识的地方信息;涉及战争、灾难和悲剧的地点保持中性,不拿这些事情开玩笑。
模型负责措辞,程序负责决定哪些事实可以进入卡片。
这比让模型自己查位置、猜地名、生成整张卡片更容易检查。原文确实描述了一个运行中的作品,但没有给出生成成功率、响应时间分布或检查节点的完整校验规则。
「当前位置的卫星图」为什么用昨天的影像
卡片中心对应 ISS 的当前位置,底图却不是空间站实时拍摄的照片。
作者使用 NASA GIBS(Global Imagery Browse Services),获取 Terra 卫星 MODIS 仪器的真彩色影像。选择昨天的数据,是因为今天的影像要在一天中逐步补齐。
昨天的数据也可能缺块。Terra 按条带扫描地球,赤道附近两次经过之间会留下没有数据的楔形区域。作者于是把无云拼接图 NASA Blue Marble 放在底层:MODIS 有数据的位置显示昨天的云层,没有数据的位置露出无云地表。
画面覆盖约 12° × 8°,大致为 1,300 × 900 km,以空间站位置为中心。它能帮助读者理解空间站飞到了哪里,但不能当作当地此刻天气的证据。
地名也需要补洞。ISS 大约每 92 分钟绕地球一圈,下方经常是海洋,Nominatim 在那里可能查不到结果。以下代码用粗略坐标框识别海洋,同时计算当地太阳时:
const { latitude: lat, longitude: lon } = $('Wo ist die ISS?').first().json;
const now = new Date();
const solar = ((now.getUTCHours() + now.getUTCMinutes() / 60 + lon / 15) % 24 + 24) % 24;
const clock = String(Math.floor(solar)).padStart(2, '0') + ':' + String(Math.floor(solar % 1 * 60)).padStart(2, '0');
function ocean(lat, lon) {
if (lat < -55) return 'southern';
if (lat > 66) return 'arctic';
if (lon > 20 && lon < 147 && lat < 30) return 'indian';
if (lon > -100 && lon <= 20 && (lat > 8 || lon > -70)) return 'atlantic';
return 'pacific';
}
const geo = $('Was liegt darunter?').first().json;
const place = geo.error ? OCEANS[lang][ocean(lat, lon)] : geo.display_name;
重点看 lon / 15:这里使用 UTC 加经度除以 15 计算太阳时,并将结果归一到 0 至 24 小时。它表达当地大致是早晨、中午还是夜晚,不对应行政时区和夏令时。ocean() 同样只是粗略分类,不能拿来做精确海域判定。
为什么限流和图片下载都放在网站侧
每次点击都会产生一次 AI 调用,因此 PHP 在请求进入 n8n 前检查额度:所有访客合计每 20 秒最多生成一张,每位访客每小时最多五张,另设每日上限。每日上限具体是多少,原文没有交代。
这个顺序很实用。已经超额的请求无需启动后续工作流,成本控制也不依赖模型配合。不过,全站共享的 20 秒间隔适合这个小作品,多人同时使用时会限制吞吐量。
图片也由网站服务器下载并保存。浏览器直接加载 NASA 图片,会把访问者 IP 随请求发送给第三方,还需要放宽内容安全策略(CSP)的图片来源。保存到自己站点后,可以继续使用 img-src 'self',卡片也不会因为上游影像变化而跟着改变。
原文还给出了迁移步骤:在 n8n 右上角菜单选择 Import from File 导入工作流 JSON;在 KI schreibt (IONOS) 中创建 Header Auth 凭据,名称为 Authorization,值为 Bearer 加 API key,并按供应商调整 base URL 和 model。
接着替换 An die Seite senden 中的 URL 和 token 占位符,启用工作流,再调用 webhook 测试:
{"id": "test000001", "lang": "en"}
要注意 id 和 lang 是这条流程的输入字段。替换末端目标时,自己的 webhook、邮件或文件接收方式,也需要配套调整。
我的判断
我认可它把 AI 放进已有数据和规则之间的做法。问候语生成失败,还能用模板交付;海洋查不到地名,就由代码补上。这些选择让应用的基本功能不完全依赖模型输出。
但「总能收到卡片」需要加上范围:原文明确处理的是模型回答为空或损坏,没有说明定位接口超时、地理编码失败、图片下载失败和回调失败时怎么办。模板兜底不能自动覆盖整条链路。
n8n 的价值在于把请求、转换、模型调用和回传连接得直观,方便观察与调整。熟悉 Node 的团队也可以用普通接口加异步任务实现相同流程。作者没有提供两种方案的成本或维护对照,这里展示的是一套可用实践,不能据此认定 n8n 更快、更便宜。
能拿来做什么
- 给现有 AI 生成功能收窄职责:由后端提供已确认的字段,让模型完成措辞,并为关键输出准备模板。
- 用 n8n 复现一次「请求生成、检查结果、保存内容、返回固定链接」的流程,再考虑用于客户主动申请的报告、培训证书或入职卡片。
- 在前端明确标出数据口径,把实时位置、昨日影像和太阳时分别说明,避免界面把不同时间的数据包装成同一个「实时」结果。
- 在网站或 Node 接口入口执行限流,并检查外部素材是否需要自行保存,让成本控制和内容留存成为业务流程的一部分。