结论:想让 AI 搜索在本地类问题里优先召回你的内容,必须把地理信号做进三层——URL 路径层、结构化数据层、正文内容层,三层同时显式声明,AI 才能在「上海 XX 服务哪家好」这类查询里把你的页面当成候选答案。只做其中一层,召回率提升有限。
GEO 地理信号是什么?和生成式引擎优化是一回事吗?
不是一回事,这是两套同名概念。GEO 在 AI 搜索圈有两个含义:一是 Generative Engine Optimization(生成式引擎优化),指让内容更容易被 AI 回答引用;二是 Geographic 地理信号,指页面里能被机器读取的地理归属信息。本文讲的是后者,它服务于前者。
AI 搜索的召回流程是「先按地理范围筛候选集,再按相关性排序」,所以地理信号的作用是让你进入候选集,而不是决定最终排名。你如果连候选集都进不去,内容写得再好也不会被引用。
怎么让 AI 判断你的内容属于哪个城市?三个信号层缺一不可
结论:URL 层决定「能不能被按城市切分」,结构化数据层决定「城市信息能不能被机器读懂」,内容层决定「读懂之后信不信」。三层的具体做法如下。
URL 路径层,把城市写进可抓取的静态路径,例如 https://www.gj0.com/shanghai/,不要让城市只存在于 Cookie 或 JS 变量里。中文站点加 hreflang 标注,zh-CN 指向大陆版本,x-default 指向默认版本。
结构化数据层,用 JSON-LD 声明 LocalBusiness,把坐标写到小数点后 6 位(约 0.1 米精度),把服务范围写进 areaServed。示例:
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "示例科技",
"url": "https://www.gj0.com/shanghai/",
"telephone": "+86-21-6000-0000",
"geo": {"@type": "GeoCoordinates", "latitude": 31.230416, "longitude": 121.473701},
"areaServed": [{"@type": "City", "name": "上海"}],
"sameAs": ["地图 POI 链接"]
}
内容层,正文首段 100 字内出现「城市名 + 服务词」,写清覆盖范围(如「覆盖上海 16 个区,外环内 2 小时上门」)、营业时间、本地电话区号。这些细节是 AI 判断「这条内容是真的本地内容」的直接依据。
城市落地页怎么避免被判重复内容?
结论:城市页必须有 60% 以上独有内容,否则会被当成模板页集群,整批降权甚至不收录。
具体做法是每城页面至少 300 字本地化正文,包含本地案例、本地价格区间、本地合作方名单、本地 FAQ 三条以上。canonical 指向页面自身,不要指回全国页——这是最常见的错误,等于告诉引擎「这页是副本」。内链结构上,城市页链向全国页和 2 到 3 个邻近城市页。站点地图按城市拆分,lastmod 精确到日期,方便引擎单独抓取单城更新。
CDN 按 IP 返回不同内容算不算作弊?
结论:内容本身按地域不同是允许的,但必须让爬虫拿到同一份真实内容,隐藏内容属于 cloaking(对用户和爬虫返回不同内容),会被直接惩罚。
安全做法是在服务端按 IP 段判断城市并返回对应城市页,同时对已知爬虫 UA 返回全部城市页的索引入口。Cloudflare 用户可以直接读 cf-ipcity 变量做分流。
怎么验证本地召回是否真的生效?
结论:用「抓取覆盖 + 引用命中」两个指标交叉验证,单看排名会误判。
第一步,用 5 个不同城市的出口 IP 或代理抓取目标页,确认返回的城市内容正确。第二步,在搜索引擎后台看地域维度的展示数据,确认城市页有独立抓取记录,而不是只抓了全国页。第三步,在 AI 搜索里逐条问「上海 XX 服务推荐」,记录你的页面被引用次数,按周统计引用率变化。第四步,查服务器日志里爬虫对 /shanghai/ 这类城市路径的访问频次,频次长期为 0 说明内链或 sitemap 没生效。
整体上,地理信号不是一次配置就完事的工作:URL 和结构化数据是地基,一次做对可以长期复用;内容层需要按城市持续补充本地案例,因为 AI 对本地内容的信任度会随细节密度上升。三层都做实后,本地类查询的召回率变化通常在 2 到 4 周内能从抓取日志里看到。