📍 无忆研习社 › 开源解码 › 一张照片如何被AI反推出拍摄位置?开源项目 geo-sleuth 原理全拆解:EXIF、天际线、桥墩测距与个人隐私防护指南

一张照片如何被AI反推出拍摄位置?开源项目 geo-sleuth 原理全拆解:EXIF、天际线、桥墩测距与个人隐私防护指南

摘要:关掉定位、不发原图就安全吗?这篇拆解开源项目geo-sleuth:AI不靠地标和文字,也能用山脊轮廓、影子方向、桥墩间距等几何线索,结合公开地理数据,把拍摄点算到街道级甚至米级。脚本负责检索打分排序,大模型只做最后判断,每个结论都有命令与证据文件。文章还点明技术短板和法律红线:无人区准确率骤降,拿它定位他人可能触碰敏感个人信息与刑事风险。
字号 100%
行距 2.05
当前可见 60% 的内容
本文由 莫潇羽@无忆研习社(www.wuyi.wiki)撰写,转载请注明出处。

快速摘要

一句话结论:现在的 AI 已经可以在照片“没有定位信息、没有文字、没有著名地标”的情况下,靠画面里的几何线索和公开地理数据,把拍摄位置推到街道级甚至米级。GitHub 上的开源项目 geo-sleuth 就是一个典型代表,它把“看图找位置”拆成二十来个单一功能的 Python 脚本,让脚本去检索、打分、排序,大模型只在最后几个候选里做判断,每一个结论都必须对应一条真实跑过的命令和一份证据文件。

它最强的地方,不是“认出”某栋楼,而是“算出”拍摄点:远山的山脊轮廓可以和高程数据比对,等间距的桥墩可以当尺子测距,影子方向可以换算成镜头朝向。它的短板也很清楚:画面里特征越少、网上公开资料越少(比如野外无人区),准确率掉得越厉害。

对普通人来说,这件事真正的意义在于两点:第一,关掉相机定位、不发原图,已经挡不住“从像素里读位置”这一类技术;第二,这类工具只能用在自己的照片、或已获授权的照片上,拿来定位他人,在国内可能触碰《个人信息保护法》中关于“行踪轨迹”这类敏感个人信息的规定,甚至涉及刑事风险。

想看完整拆解,往下翻。

写在正文前:本文介绍 geo-sleuth 的技术原理,目的是帮助读者理解“照片为什么会暴露位置”,从而更好地保护自己。文中不提供任何针对特定个人的定位方法或案例演示。如将相关技术用于定位他人,可能触犯《个人信息保护法》《刑法》等法律法规,后果由使用者自行承担。

一、先把问题想清楚:一张照片里,位置信息藏在哪几层

我自己琢磨这类工具的时候,习惯先不看代码,而是先问一个朴素的问题:一张照片凭什么能暴露位置?

想明白这个,后面所有原理都好理解。我把它拆成四层,越往下越隐蔽,也越难防。

1.1 第一层:照片文件里的“隐形档案”——EXIF

EXIF 是“可交换图像文件格式”的缩写,你可以把它理解成照片的身份证:它不是画面的一部分,而是写在图片文件里的一段附加数据。手机拍照时,会自动往里塞拍摄时间、手机型号、光圈快门等参数,如果你给相机开了定位权限,经纬度坐标也会一并写进去。

这一层其实早就不是新闻了。几年前“微信发原图会不会泄露位置”就上过热搜,当时微信团队回应的意思是:任何智能手机拍的照片都带有 Exif 参数,可以记录位置、时间等信息,无论通过什么工具发送原图,这些附带信息都会一起发出去;而朋友圈发布的照片会经过系统压缩,不带位置信息。同一篇报道里,法律学者还总结过一个很实用的判断标准:原图泄露拍摄位置,需要同时满足手机定位打开、相机设置了保存地理位置、发送的是原图这三个条件。

所以第一层的结论很简单:EXIF 是最直接的泄露渠道,但也是最好防的。 关掉相机定位、不发原图,基本就堵住了。

问题在于,下面三层跟 EXIF 一点关系都没有。

1.2 第二层:画面里能“读”出来的字

招牌、路牌、车牌、公交站名、店铺门头上的电话号码……这些都是可以被 OCR(光学字符识别,就是让电脑把图片里的字认出来)读取的信息。

举个通俗的例子:一张街景照片里,远处有块不太清楚的门头,只露出半截电话号码,区号那几位就足以把范围缩到某个城市;车牌第一个字是省份简称、第二个字母对应地级市,这又是一条硬线索。

这一层比 EXIF 难防,因为很多人发照片时根本不会注意背景里有什么字。

1.3 第三层:能被“搜”出来的地物

即使画面里一个字都没有,只要出现了有辨识度的东西——一座造型特别的塔、一处网红打卡点、一个风格独特的门头——就可以拿去做反向搜图(以图搜图,把图片丢给搜索引擎,让它找相似的图)。

网上公开的旅游攻略、点评网站、短视频平台,积累了海量带地点标注的照片。你拍的景点,很可能早就有成百上千人拍过、标注过。反向搜图本质上就是在这个巨大的“已标注图库”里找最像的那几张。

这一层的特点是:越出名的地方越好找,越普通的地方越难找。

1.4 第四层:藏在几何关系里的“指纹”

这是最隐蔽、也是 geo-sleuth 这类新工具真正厉害的地方。

一张照片里,就算没有字、没有地标、搜图也搜不到,它依然包含大量几何信息:

  • 远处山脊的起伏轮廓,从不同位置看过去形状完全不同,像指纹一样;
  • 影子的长度和方向,和太阳位置有确定的数学关系;
  • 一排等间距的电线杆、桥墩、路灯,在画面里“近大远小”的变化规律,能反推出它们离相机多远;
  • 道路、河流、铁路、海岸线之间的相对位置关系,可以和地图数据逐一比对。

这些东西不靠“认”,靠“算”。我觉得这才是理解这类工具的关键:它不是一个更聪明的“看图猜地方”,而是把几何测量和地理数据库接到了一起。

下面这张表把四层信息做个对照,方便你建立整体印象:

信息层

典型线索

提取方式

普通人防护难度

文件元数据

GPS 坐标、拍摄时间、机型

读取 EXIF

低(关定位、不发原图即可)

画面文字

招牌、路牌、车牌、电话区号

OCR 文字识别

中(需要留意背景、打码)

可搜地物

地标建筑、网红景点、特色门头

反向搜图

中偏高

几何关系

山脊线、影子、等间距结构、地物布局

几何计算 + 地理数据比对

高(几乎无法靠设置规避)

用一张流程图表示,大概是这样一个“由浅入深”的过程:

flowchart TD
    A[一张照片] --> B{有没有 EXIF 定位?}
    B -- 有 --> Z[直接得到坐标]
    B -- 没有 --> C{画面里有可识别文字?}
    C -- 有 --> D[OCR 读取 → 区号/车牌/地名查表]
    C -- 没有 --> E{有没有辨识度高的地物?}
    E -- 有 --> F[反向搜图 → 候选地点]
    E -- 没有 --> G[几何线索: 山脊线 / 影子 / 等间距结构]
    D --> H[候选区域]
    F --> H
    G --> H
    H --> I[地图与高程数据比对 → 收敛到少数候选]
    I --> J[输出坐标 ± 误差半径]

理解了这四层,你再去看 geo-sleuth 的设计,就会发现它基本就是把这四层按顺序、用脚本一层层榨干。


二、geo-sleuth 是个什么项目:定位、技术栈和官方案例

2.1 一句话定位:给 AI 编程助手装的“地理侦探技能包”

geo-sleuth 是 GitHub 用户 Oldcircle 发布的开源项目,项目自我介绍是一个 Agent Skill(智能体技能),作用是找出一张照片的拍摄地点,并展示推理过程,手段包括 OpenStreetMap 几何数据、高程天际线、卫星影像和街景。

这里有个概念需要先说清楚:Agent Skill 是什么?

现在很多 AI 编程助手(比如在终端里帮你写代码、跑命令的那类工具)都支持“技能”机制:一个技能就是一个文件夹,里面有一份说明书 SKILL.md,告诉 AI“遇到什么任务、该按什么步骤做、可以调用哪些脚本”,再配上实际干活的脚本。AI 读了说明书,就会按规矩办事。

geo-sleuth 就是这样一个文件夹。按照官方说明,它是标准的 Agent Skill 格式,一个文件夹里装着 SKILL.md、scripts/、references/ 和 data/,因此可以在多款主流 AI 编程助手中通用。

项目的时间线也值得一提。有海外科技资讯网站在 2026 年 9 月 19 日报道过这个项目,据该报道,它上线首日星标即过百。截至 2026 年 10 月中旬写稿时,我查看仓库页面显示的星标约 1.2k,分支(Fork)一百多个。

开源项目的星标数、版本号变化很快,上面是写稿时的近似情况,请以 GitHub 仓库页面实时数据为准。

2.2 它的设计哲学:脚本负责算,模型只负责判

我读 README 时,印象最深的不是哪个脚本多牛,而是它对“AI 该干什么、不该干什么”的划分。

用大模型直接看图猜地点,最大的问题是幻觉:模型会非常自信地给你一个答案,而且讲得头头是道,但你根本不知道它是真看出来了,还是编的。geo-sleuth 的思路是反过来的——尽量不让模型“猜”。

官方把工作分成三层:由脚本来决定和感知、负责检索与排序,模型只在排在最前面的少数几个候选之间做判断。而且它立了一条硬规矩:每个结论都必须指向会话中真实运行过的某条命令,以及这条命令生成的文件;地点的人口多少、名气大小都不能算作证据。

最后这半句我特别认同。很多人用 AI 看图定位翻车,往往就是模型倾向于往“有名的地方”猜——因为训练数据里有名地方的照片多。把“名气”明确排除在证据之外,等于从制度上堵住了这类偏差。

用表格对比一下两种思路:

对比项

让大模型直接看图猜

geo-sleuth 的做法

谁在找候选

模型凭“印象”联想

脚本在地图、高程、卫星数据里系统检索

谁在排序

模型凭感觉

脚本按似然比、相似度打分

模型的角色

从头猜到尾

只在前几名里做最终判断

结论能否核查

基本不能

每条结论对应命令和证据文件

输出形式

一个地名

坐标 ± 误差半径、相机朝向、证据图、分级置信度

2.3 技术栈:二十个“一事一脚本”的小工具

README 里列了一张工具清单,我按功能把它们归成几类,方便新手理解每个脚本是干嘛的:

类别

代表脚本

干的事(白话版)

环境自检

doctor.py

检查 Python、uv、浏览器、网络是否就绪

信息榨取

exif.py ocr.py revimg.py intake.py imgprep.py

读元数据、认字、反向搜图、裁剪放大局部

查表推理

clues.py gazetteer.py poi.py

车牌前缀、电话区号、行政区划、地名查坐标

候选管理

board.py

维护候选清单,按证据加减权重、排除、排序

物理与几何

sun.py geo.py pose.py

太阳与影子、方位与距离、相机姿态

地图与地形

osm.py terrain.py tiles.py

查地图要素、算天际线、拼卫星瓦片

视觉比对

sat_scan.py match.py gsv.py baidu_pano.py

卫星图打分、街景全景图比对

出报告

evidence.py

生成卫星底图 + 相机视野扇形的证据图

其中有几个脚本背后用到的模型值得单独解释一下:

  • CLIP:OpenAI 开源的图文匹配模型,能判断“一张图和一段文字描述有多像”。在这个项目里,它被用来给卫星图的每个格子打分,比如“这个格子里像不像有个田径跑道”。
  • DINOv2:Meta 开源的视觉特征模型,擅长提取图像的整体“长相特征”,适合比较两张照片拍的是不是同一个场景。
  • SIFT:一种经典的图像特征点算法,能在两张图里找出相互对应的角点、边缘,对应点越多,说明越可能是同一处。

官方说明里,match.py 正是把这两者结合起来,用 DINOv2 的全局相似度加上 SIFT 的匹配内点,对候选的地面照片进行排序。

数据源方面,项目致谢里列出了 OpenStreetMap、AWS 提供的高程瓦片、一份中国行政区划的开源数据集,以及上面两个视觉模型。这里有个细节:仓库本身不附带 OSM 数据,脚本是实时查询的,发布查询结果时需要注明“© OpenStreetMap 贡献者”。这一点对打算拿结果做内容的朋友很重要,后面合规那章我会再展开。

2.4 官方案例拆解:一张“什么都没有”的照片怎么被定位到米级

README 里放了一个完整案例,我觉得是理解整个项目最好的入口。我用自己的话把它的逻辑讲一遍。

照片的条件非常苛刻:EXIF 已被清除,画面里是一片收割过的稻田、远处一座长长的高架桥、右边一座陡峭的山,一个字都没有。

整个推理过程是一个“漏斗”,候选数量一路收窄:

flowchart LR
    A["读图<br/>判断: 电气化铁路、南方、霜降前"] --> B["区域扫描<br/>2.7 万段铁路桥"]
    B --> C["地平线筛选<br/>剩 171 处"]
    C --> D["天际线拟合<br/>1.4 万个机位 → 22 个"]
    D --> E["叠图人工复核<br/>剩 1 处"]
    E --> F["桥墩测距<br/>定位到约 ±2 米"]

拆开来看,每一步用的都是前面说的“第四层几何线索”:

读图阶段,模型从画面里提取出几个关键判断:高架桥上的杆子是接触网立柱,说明是电气化铁路;稻子收了但草还绿,说明还没下霜。官方把这一步得出的“华南地区”明确标注为一个押注,而不是证明。这个措辞我很喜欢,推理的不确定性被如实记录了下来。

区域扫描阶段,脚本从 OpenStreetMap 里拉出该区域所有铁路桥,共 27,335 段,每隔几百米取一个点,用高程数据算出从这个点看出去的 360 度地平线,只保留“附近地面平坦、几公里内有座明显的山、旁边地平线又是平的”这类点位。

天际线拟合阶段,在每个候选点附近放置大量假想机位,渲染出从每个机位看过去的山脊线形状,和照片里的山脊线比对。

叠图复核阶段,把排名前三的山脊线画回照片上。官方提到,排第一的那个位置分数高,其实是因为有个山包恰好被画面里的一个物体挡住了,看着吻合;排第三的那个在照片是平的地方却是斜的;反倒是排第二的那个从山脚到画面边缘全程吻合。这一步特别能说明“脚本排序 + 人或模型复核”的必要性——纯靠分数会选错。

桥墩测距阶段,照片里能数出 17 个桥墩,每个桥墩对应一条从相机出发的方位线,这些方位线和铁路线相交,交点之间的间距理应是均匀的。只有一个相机位置能让间距均匀,于是位置被锁定。

按官方说法,整个过程端到端大约 72 分钟,其中差不多一半时间是在等计算。

我读完这个案例的感受是:这已经不是“AI 看图”了,更像是一个把测绘、天文、计算机视觉串起来的自动化调查流水线。下一章我就把其中几个关键原理掰开讲,尽量讲到新手也能自己在纸上算一算的程度。


三、核心原理逐层拆:从“看图猜地方”到“用几何算位置”

这一章是整篇文章的硬核部分。我尽量不堆术语,每个原理都配一个能在纸上算的小公式。公式统一用普通文字和符号来写,复制到金山文档、公众号后台或者网页里都能正常显示。

3.1 影子与太阳:一根电线杆就是一个“日晷”

先说最容易理解的一个。

太阳在天上的位置,可以用两个角度描述:高度角(太阳离地平线有多高)和方位角(太阳在正北顺时针转了多少度)。这两个角度只取决于三件事:地点的纬度和经度、日期、时刻。天文学上这是一套早就算得清清楚楚的公式,网上很多开源库都能直接调用。

反过来想,如果照片里有一根竖直的杆子和它的影子,我们就能量出太阳的高度角:

tan(太阳高度角) = 杆子高度 ÷ 影子长度

举个数字例子:一根大约 3 米高的路灯杆,影子大约 5 米长,那么 tan(高度角) ≈ 0.6,高度角约 31°。

拿到高度角能干嘛?如果你大致知道拍摄日期(比如 EXIF 里没有坐标,但时间还在;或者从植被、衣着推测出季节),那么同一个高度角只可能出现在特定的纬度带和特定的时段里。这就是 geo-sleuth 里 sun.py 干的事情之一:用影子推纬度范围、推一天中的时刻。

影子还有一个更实用的作用:定方向。影子总是背对太阳,所以只要知道大概时间,影子的指向就能换算成真北方向,进而推出镜头朝哪边拍。有了朝向,后面所有和地图比对的工作都能省掉一大半。

flowchart LR
    A[影子长度与杆高] --> B[太阳高度角]
    C[影子方向] --> D[太阳方位角]
    B --> E{结合日期}
    D --> E
    E --> F[可能的纬度带]
    E --> G[镜头朝向 / 街道走向]

我自己的理解是,影子这条线索单独用往往只能给出“一条带子”而不是“一个点”,它的价值在于和别的线索交叉,把范围切得更窄。

3.2 天际线:远山的轮廓就是地理“指纹”

这是 geo-sleuth 最有代表性的一招。

天际线,就是照片里天空和地面(山、楼)交界的那条轮廓线。在山区或丘陵地带,这条线的起伏形状高度依赖拍摄位置:你往左挪一公里,远处的山峰会换个角度,近处的小山包可能会把远处的主峰挡住,轮廓就完全变了。

而全球的地形起伏早就有公开的数字高程模型(DEM,可以理解成一张“每个格子记录海拔”的大表格)。有了 DEM,就能用计算机模拟“站在某个点,往四周看出去,地平线长什么样”。

算法上大致是这么个思路,我写成伪代码方便理解:

# 伪代码:从某个候选点渲染 360° 地平线
def render_horizon(lat, lon, eye_height, dem):
    horizon = []
    for azimuth in range(0, 360):           # 每隔 1° 看一个方向
        max_angle = -90
        for dist in range(100, 30000, 100):  # 沿这个方向每 100 米取一个点,看到 30 公里外
            h = dem.elevation_at(lat, lon, azimuth, dist)
            angle = atan((h - eye_height) / dist)  # 该点相对眼睛的仰角
            max_angle = max(max_angle, angle)      # 只有最高的那个能被看到
        horizon.append(max_angle)
    return horizon  # 360 个仰角,组成一条“地平线曲线”

然后把照片里提取出的那段山脊线(只占 360° 里镜头视野那几十度),在这条曲线上左右滑动,找最吻合的位置。吻合度可以简单理解为:

误差 = Σ |照片仰角(i) − 模拟仰角(i)| ÷ 采样点数

误差越小,说明这个候选点越可能是拍摄点。

这里有两个现实问题,我觉得值得说一下:

一是计算量大。一个省级范围随便撒点就是几万个候选点,每个点算 360 度、每个方向几百个采样,这就是为什么官方案例要先用“附近有铁路桥”“地面平坦”这类硬条件把点位砍到几百个,再做精细拟合。

二是容易被遮挡骗。前面讲官方案例时提过,分数第一的候选其实是因为一个山包恰好藏在前景物体后面。所以天际线拟合之后,必须把模拟的山脊线叠回照片上人工看一眼,这一步机器省不掉。

3.3 等间距结构:桥墩、电线杆、路灯就是一把尺子

这是我觉得最巧妙的一个思路。

高铁桥、高速高架桥的桥墩,跨度在设计上往往是标准化的,常见的就是几十米一跨;电线杆、路灯、行道树在一条路上也往往等间距排布。真实世界里等距的东西,拍到照片里会因为透视变得“近大远小”,越远挤得越密。

小孔成像模型给出一个很朴素的关系:

物体到相机的距离 ≈ 镜头焦距 × 物体实际间距 ÷ 画面上的间距

焦距可以从 EXIF 的“等效焦距”拿到;没有 EXIF,也可以从常见手机主摄的视角范围去估一个区间。只要再假设一个桥墩实际跨度(官方案例里也明确写了“跨度为假设值”),画面上相邻桥墩的像素间距就能换算成距离。

更进一步,geo.py 有个“从等间距结构反推相机位置”的功能,原理我用白话讲一下:

  1. 照片里每个桥墩对应一条从相机发出的视线,这条视线的方向(方位角)可以根据它在画面里的横向位置算出来;
  2. 把这些视线画到地图上,它们会和地图里的那条铁路线相交;
  3. 如果相机放对了位置,这些交点之间的距离应该刚好相等(因为桥墩本来就是等距的);
  4. 相机放错了,交点间距就会忽大忽小。

于是问题变成:在地图上找一个点,让所有视线和铁路线的交点尽量均匀。这是一个标准的最优化问题,计算机很擅长。

线索类型

能得出什么

依赖的前提

影子

纬度带、时刻、镜头朝向

大致日期、竖直参照物

天际线

候选机位排名

有明显山体、DEM 覆盖

等间距结构

距离、精确机位

结构确实等距、能在地图上找到对应线状物

地物共现

候选区域

地图数据标注完整

3.4 地物共现:让地图数据库替你“排除不可能”

OpenStreetMap(简称 OSM)是一个全球志愿者共同编辑的开放地图,里面不只有道路,还标注了铁路、河流、电力线、学校、加油站、水塔、跑道等等。它提供一个叫 Overpass 的查询接口,可以写类似“数据库查询语句”的东西,问它:

在这个范围内,找出所有“铁路桥 500 米内有河流、并且 2 公里内有一座山峰”的位置。

这就是地物共现:照片里同时出现了 A、B、C 三样东西,那拍摄点附近一定同时存在这三样。每多一个条件,候选位置就少一大截。geo-sleuth 的 osm.py 就是专门干这个的。

一个简化的查询思路长这样(示意,非项目原始代码):

[out:json];
// 在指定范围内找铁路桥
way["railway"="rail"]["bridge"="yes"](区域范围) -> .bridges;
// 找这些桥附近 500 米内的河流
way["waterway"="river"](around.bridges:500) -> .rivers;
// 输出同时满足条件的桥
.bridges out geom;

我觉得这个思路特别值得普通开发者学习:它不是在“找答案”,而是在“排除不可能”。排除得够狠,剩下的自然就是答案。

3.5 视觉比对:CLIP 扫卫星图,DINOv2 + SIFT 比街景

前面几种方法都是“算”,但总有一些线索没法用公式表达,比如“这里看起来像一个工厂”“那片屋顶的颜色排布”。这时候就轮到视觉模型出场了。

卫星图扫描这一步,项目把一大片区域切成很多小格子,用 CLIP 给每个格子打分,问的问题类似“这个格子里有没有田径跑道”。项目 README 的基准测试部分公布过一组数据:在深圳一片 4×8 公里、364 个格子的区域里,以 OSM 上标注的 40 个跑道为真值,排名前 100 的格子里能找回其中 32 个。这个成绩说明 CLIP 很适合做初筛,把需要人看的格子从几百个压到几十个,但离“一眼命中”还差得远。

街景比对这一步,项目会拉取候选点附近的全景街景图(国内用的是百度全景),渲染出不同朝向的画面,然后用 DINOv2 比整体像不像、用 SIFT 数能对上的特征点有多少,综合排名。同样来自 README 的基准测试:在深圳的 8 个测试案例里,正确答案全部排进了前 6 名,其中一半排在第 1。需要说明的是,这两组都是单个脚本的专项测试,不是整套流程的定位准确率,后面第五章会再提到这一点。

用一张图把视觉比对的两条线路画出来:

flowchart TD
    A[候选区域] --> B[切成卫星瓦片网格]
    B --> C[CLIP 打分: 像不像目标地物]
    C --> D[前几十个格子]
    A --> E[拉取候选点附近街景全景]
    E --> F[按不同朝向渲染画面]
    F --> G[DINOv2 整体相似度]
    F --> H[SIFT 特征点匹配数]
    G --> I[综合排序]
    H --> I
    D --> J[人或模型复核]
    I --> J

3.6 候选看板:把“感觉”变成可计算的证据账本

最后讲一个容易被忽略、但我认为是整个项目灵魂的设计:board.py,也就是候选看板。

它用的思想叫似然比(Likelihood Ratio),说白了就是:“如果答案真是 A 地,看到这条线索的可能性,比答案不是 A 地时,高出多少倍?”

似然比 = P(看到这条线索 | 是该地点) ÷ P(看到这条线索 | 不是该地点)

每来一条线索,就给每个候选乘上对应的似然比:

更新后的几率 = 原来的几率 × 似然比₁ × 似然比₂ × … × 似然比ₙ

比如画面里有接触网立柱,那“有电气化铁路的区域”似然比就高,“没有铁路的区域”似然比接近 0。几条线索乘下来,候选之间的差距就拉开了。

这套方法有几个好处:

  • 可追溯:每个候选为什么排第几,账本里写得清清楚楚;
  • 有分寸:README 里定的规矩是,排除一个候选必须有读出来或算出来的硬证据,而观察和猜测只能降低权重,不能一票否决;
  • 防止拍脑袋:模型想说“我觉得是这里”,必须先在账本里找到支持它的那几条记录。

我做内容这几年,最怕的就是“结论漂亮但说不清来路”。这个看板的设计,某种程度上就是把调查记者那套“每句话都要有出处”的纪律,写成了代码。莫潇羽在这里多说一句:这个思路远比定位本身更有迁移价值,做数据核查、做事实验证、甚至做自己的选题判断,都可以借鉴。


四、上手体验:环境准备、安装与一次“只用自己照片”的合规自测

讲完原理,聊聊怎么动手。先把丑话说在前面:这一章的所有操作,我只建议你用自己拍的照片、或者明确获得授权的照片来做。 原因第七章会细讲,这里先记住这条底线。

4.1 先确认你的“底座”:它不是一个独立软件

很多人一听“开源工具”,第一反应是下载个安装包双击运行。geo-sleuth 不是这样的东西。

它是一个技能包,必须装进一个能读 SKILL.md、能执行命令行的 AI 编程助手里才能工作。官方列出的兼容对象包括 Claude Code、Codex、Cursor、Gemini CLI、OpenCode、GitHub Copilot,以及其他遵循同样规范的智能体。

这里给国内朋友一个合规提醒:上面这些编程助手大多是境外厂商的产品,背后调用的也是境外大模型。境外模型使用需遵守国内网络与内容管理相关规定,涉及工作数据、个人照片时尤其要谨慎,不要把敏感内容上传到你无法确认数据去向的服务里。国内也有不少支持类似“技能/规则文件”机制的编程助手和大模型服务,选择时优先考虑数据存储在境内、有明确隐私政策的产品。

整体结构可以这样理解:

flowchart TD
    A[你] -->|自然语言: 帮我看看这张照片在哪拍的| B[AI 编程助手]
    B -->|读取| C[SKILL.md 操作规范]
    B -->|执行| D[scripts/ 下的 Python 脚本]
    D --> E[本地处理: EXIF / OCR / 裁剪]
    D --> F[联网查询: 地图 / 高程 / 搜图 / 街景]
    D --> G[本地模型: CLIP / DINOv2]
    E --> H[证据文件]
    F --> H
    G --> H
    H --> B
    B -->|报告| A

4.2 运行环境清单

根据官方 README,跑起来需要这些东西:

依赖

作用

备注

Python 3.10 及以上

运行全部脚本

建议用独立虚拟环境

uv

Python 包与环境管理工具

每个脚本自带依赖声明,首次 uv run 时自动安装

curl

命令行下载工具

多数系统自带

Chrome 或 Playwright Chromium

反向搜图时驱动浏览器

没有的话反向搜图不可用,其他功能照常

足够的磁盘空间

存放视觉模型权重

CLIP、DINOv2 首次使用时会下载,几百兆到上 G 不等

uv 这个词新手可能没见过,简单说它就是一个更快的 Python 包管理器,作用和 pip 类似,但能自动帮每个脚本隔离出独立的依赖环境,避免“装了这个包、那个脚本就坏了”的问题。

4.3 安装:两种方式任选

官方给的一键安装方式,是用一个叫 skills 的命令行工具把技能装进指定的编程助手:

# 交互式安装:运行后按提示选择你用的编程助手
npx skills add Oldcircle/geo-sleuth

npx 是 Node.js 自带的命令,意思是“临时下载并运行一个 npm 包”,所以你的电脑需要先装 Node.js。

安装前提示:上面的命令会从 npm 公共仓库和 GitHub 下载代码,脚本首次运行时还会下载依赖包和模型权重,部分资源托管在境外。该命令会从境外服务器下载代码和依赖,执行前请自行评估网络合规性;如访问不畅,可优先使用国内镜像源(npm、Python 依赖均可按官方文档配置),不要采用任何突破网络管理的手段,访问不了的资源就放弃,项目大部分本地功能不受影响。另外,任何第三方代码都建议先读一遍再运行,最好放在独立的测试环境里。

如果你只用其中一两个助手,交互式安装最省心,不必一次全装。习惯手动管理的话,也可以直接把仓库克隆下来,把 skills/geo-sleuth 这个文件夹复制到对应助手的技能目录里。不同助手的技能目录不一样,以官方 README 的对照表为准,比如 Claude Code 用户级目录是 ~/.claude/skills/,Codex 等几款则共用 ~/.agents/skills/。

开源项目的安装方式、目录约定迭代很快,上面是写稿时的情况,请以项目仓库 README 的最新说明为准。

4.4 第一步永远是“体检”

装好之后别急着丢照片。官方建议首次使用先跑环境自检脚本:

# 本地检查:Python、uv、curl、查表数据、目录写权限、浏览器能否启动
uv run skills/geo-sleuth/scripts/doctor.py

# 附加网络探测:只探测各服务能否连通,不上传任何照片
uv run skills/geo-sleuth/scripts/doctor.py --network

如果是通过安装命令装的,路径换成技能实际所在目录即可,也可以直接让编程助手替你跑。

我的经验是,这一步能帮你提前知道哪些功能在你的网络环境下用不了。项目里有些数据源和服务部署在境外(比如部分反向搜图引擎、海外街景服务、模型权重托管平台),在国内网络环境下可能连不上或不稳定。遇到这种情况,正确的做法是接受这个限制,或者改用国内可正常访问的服务(比如项目本身就支持的百度反向搜图和百度全景),不要去折腾任何突破网络管理的手段。

4.5 推荐的自测方式:先跑“纯本地”流程

如果你只是想理解这套流水线在干什么,我强烈建议先从不联网搜图的本地模式开始。官方提供了这样一条命令:

# 只做本地处理:读元数据、裁剪边角、生成变体、OCR,不做反向搜图
uv run skills/geo-sleuth/scripts/intake.py photo.jpg --out-dir intake --no-rev

跑完之后,打开 intake/intake.md,你会看到这张照片被“榨”出了哪些信息:EXIF 里有什么、哪些区域被单独裁剪放大、OCR 读出了哪些字。

我觉得这一步对普通人的意义,甚

🔒
该内容仅对更高等级社区用户开放
请谨慎解锁时效性强且发布日期较早的文章
单篇解锁后若未显示全文请刷新页面
您当前:游客 · 可见 60% 内容 · 升级至 注册用户 可见 70%
👀
游客
可见 60%
✓ 当前
注册用户
注册用户
可见 70%
社区精英
社区精英
可见 100%
社区守护
社区守护
可见 100%
仅解锁本文,永久有效。如需PDF珍藏版,请联系站长获取。 当前单篇价格 ¥9.9
✏️ 发表评论

请先登录后发表评论

前往登录
📊 站点统计
今日发布0 篇
文章总数1107 篇
昨日发布1 篇
本月发布31 篇
建站时间456 天
🔍 搜索
站长微语

联系站长

QQ:2805463528
AIGC 技术社区
致力于解码 AI前沿技术 与经验分享
纯粹的技术应用社区

💡 欢迎您的建议与反馈,让社区变得更好

快速通道
联系站长
站长QQ二维码
AI交流群
AI交流群
仍在路上

那些寒夜里追赶过的方向

那些冷眼下没放弃的理想

一篇一篇写到现在

仍在路上

"不羁放纵爱自由"

—— 致敬 Beyond
持续创作中 莫潇羽 · 无忆研习社
▲