1. 当AI搜索对你的网站视而不见时,问题出在哪
你可能已经注意到了这个现象:辛辛苦苦写的技术博客、产品文档、知识库文章,在传统搜索引擎里排名还行,但一到AI搜索场景——比如各类AI助手、智能问答、对话式检索——你的内容就像从未存在过一样,AI在回答相关问题时,引用的全是别人的页面,甚至是一些质量远不如你的内容。这不是玄学,也不是AI“看你不顺眼”,而是一套非常具体的抓取、解析、引用机制在起作用。
GEO,全称Generative Engine Optimization,中文一般叫“生成式引擎优化”,是最近一两年随着AI搜索普及而快速升温的一个实战方向。它和传统SEO有交集,但底层逻辑差别很大。传统SEO的核心是“让搜索引擎的爬虫抓到你的页面,然后给你一个好排名”,而GEO的核心是“让AI在生成回答时,愿意引用你的内容作为依据”。这两件事的评判标准、技术路径、优化手段都不一样。很多人把SEO那套直接搬过来做GEO,结果发现完全跑不通,原因就在这里。
这篇文章要解决的问题非常具体:为什么AI搜索不引用你的网站,以及你能用哪些代码层面的手段去改掉它。适合的读者包括独立开发者、技术博主、中小团队的内容负责人,以及任何希望自己的网站在AI搜索时代获得引用流量的人。我不打算讲空泛的“内容为王”大道理,而是从抓取协议、文件配置、结构化数据、内容分块这几个可落地的技术点切入,把每一步的原理和操作都拆开讲清楚。你不需要是搜索引擎专家,只要你能改服务器上的文件、能写基本的HTML和JSON,这篇内容就能直接照着做。
在展开之前,先明确一个认知:AI搜索不引用你,通常不是单一原因造成的,而是“抓不到、读不懂、信不过、用不上”这四个环节里至少有一个断了。接下来的章节,我会按照这个链路逐一拆解,每个环节都给出具体的排查方法和代码级的修复方案。
2. 先搞清楚AI搜索的抓取链路和你的内容在哪一步掉了队
2.1 从爬虫到引用:AI搜索处理内容的四个阶段
要解决问题,先得知道问题发生在哪。AI搜索处理一个网页,大致经历四个阶段,每个阶段都有对应的“淘汰机制”。
第一个阶段是发现与抓取。AI搜索的爬虫需要先知道你的页面存在,然后发起请求把内容拉回去。这个阶段最常见的淘汰原因是robots.txt把爬虫挡在了门外,或者服务器响应太慢、返回错误码,导致爬虫放弃。很多网站的robots.txt是从几年前抄来的模板,里面可能无意中屏蔽了大量路径,自己却完全不知道。
第二个阶段是解析与理解。爬虫把HTML拉回去之后,需要从中提取正文、识别结构、判断主题。如果你的页面是纯JavaScript渲染的,而爬虫不执行JS,那它拿到的就是一个空壳;如果你的正文混杂在大量导航、广告、侧边栏里,解析器可能提取出一堆噪声,根本抓不到核心内容。
第三个阶段是信任与筛选。AI搜索不会随便引用一个页面,它会综合判断这个来源是否可信、是否权威、是否与问题相关。这个判断依赖很多信号:域名历史、内容质量、是否有结构化数据标注、是否被其他可信来源引用等。一个没有任何结构化标注、内容主题模糊的页面,在这一步很容易被过滤掉。
第四个阶段是分块与引用。AI生成回答时,不是整页整页地读,而是把内容切成小块,找到与问题最匹配的片段来引用。如果你的内容是一大段没有层次的长文,或者关键信息藏在图片里、藏在需要交互才能展开的折叠面板里,那AI就很难定位到可引用的片段。
把这四个阶段串起来看,你会发现“AI不引用你”可能发生在任何一个环节。所以排查的时候不能瞎猜,要按链路顺序逐一验证。
2.2 用命令行快速验证你的页面是否被抓取
在改任何东西之前,先做一次基础体检。最直接的方法是模拟爬虫请求,看看服务器返回了什么。打开终端,用curl命令请求你的页面,观察返回状态码和内容:
curl -I -A "Mozilla/5.0 (compatible; AISearchBot/1.0)" https://your-site.com/your-page这里的关键是-A参数,它模拟了爬虫的User-Agent。很多网站会根据UA返回不同的内容,用浏览器UA测试是测不出问题的。你需要关注几个点:返回码是不是200,Content-Type是不是text/html,有没有异常的X-Robots-Tag响应头。
接着,把返回的HTML拉下来,看看正文是否真的在HTML里:
curl -s -A "Mozilla/5.0 (compatible; AISearchBot/1.0)" https://your-site.com/your-page | grep -i "你的核心关键词"如果这条命令什么都没输出,说明你的正文内容不在初始HTML中,很可能是靠JavaScript动态渲染的。这就是第二阶段“解析与理解”掉队的典型信号。AI爬虫对JS渲染的支持程度参差不齐,依赖JS渲染的内容被抓取到的概率会大幅降低。
还有一个容易被忽略的检查点:看看你的页面有没有返回noindex或nofollow相关的meta标签。有些建站工具或插件会默认加上这些标签,站长自己都不知道。用一条命令就能查:
curl -s https://your-site.com/your-page | grep -i "robots"如果输出里出现了noindex,那你的页面等于主动告诉AI搜索“别引用我”。这个问题在WordPress、某些CMS系统里非常常见,尤其是当你在后台勾选了“ discouraging search engines”之类的选项时。
2.3 robots.txt里那些悄悄挡住AI爬虫的规则
robots.txt是抓取链路的第一道门。问题在于,很多网站的robots.txt写于很多年前,当时根本没有AI搜索爬虫这个概念,但里面的规则却可能误伤现在的AI爬虫。
先看一个典型的“误伤”案例。假设你的robots.txt里有这么一段:
User-agent: * Disallow: /api/ Disallow: /search/ Disallow: /*?*这段规则的本意是屏蔽API接口、站内搜索结果页和带参数的URL。但问题在于,很多内容页的URL可能带有查询参数(比如用于追踪来源的?ref=),或者你的文章路径恰好包含/search/这样的词。结果就是,AI爬虫严格遵守规则,把这些页面全部跳过了。
更隐蔽的问题是User-agent的匹配逻辑。有些AI爬虫的UA名称里包含通用词汇,而你的robots.txt里可能有一条针对某个通用词的Disallow规则,无意中把它也挡住了。排查方法是把你的robots.txt完整读一遍,逐条问自己:这条规则会不会误伤正常内容页?
修复的原则很简单:对AI搜索爬虫做精确放行,而不是依赖通配符。如果你不确定某个爬虫的UA,最稳妥的做法是显式允许主流AI爬虫,同时保持对恶意爬虫的屏蔽。下面是一个参考写法:
User-agent: * Disallow: /admin/ Disallow: /private/ Allow: / User-agent: GPTBot Allow: / User-agent: ClaudeBot Allow: / User-agent: PerplexityBot Allow: /注意,这里的Allow: /要放在Disallow规则之后,因为robots.txt的匹配遵循“最长匹配优先,相同长度时Allow优先”的原则。这样写的好处是,通用规则屏蔽敏感路径,但AI爬虫被显式放行,不会因为通配符规则被误伤。
改完robots.txt之后,一定要用工具验证。可以直接在浏览器里访问https://your-site.com/robots.txt确认文件生效,也可以用Google Search Console的robots.txt测试工具(虽然它是给传统搜索用的,但匹配逻辑是通用的)来模拟不同UA的抓取结果。
3. llms.txt:给AI搜索看的“内容说明书”
3.1 llms.txt到底是什么,为什么它和robots.txt不是一回事
如果说robots.txt是“门禁规则”,告诉爬虫哪些能进哪些不能进,那llms.txt更像是“内容说明书”,主动告诉AI搜索“我的网站有哪些内容、分别讲什么、推荐你优先看哪些”。这是一个相对较新的约定,目前还没有形成像robots.txt那样的强制标准,但已经被越来越多的AI搜索和内容平台纳入参考。
llms.txt的核心思路是:与其让AI爬虫自己瞎逛、从一堆导航和广告里猜你的核心内容,不如你主动提供一个结构化的清单,用Markdown格式列出你网站的重要页面、每页的主题摘要、以及推荐的内容分块。这样AI在处理你的网站时,能更快地定位到高质量内容,引用你的概率自然就上去了。
它和robots.txt的本质区别在于:robots.txt是“防御性”的,解决的是“能不能抓”的问题;llms.txt是“进攻性”的,解决的是“抓什么、怎么理解”的问题。两者配合使用,才能让AI搜索既进得来,又看得懂。
3.2 llms.txt的完整写法与字段说明
llms.txt的格式并不复杂,本质上是一个Markdown文件,放在网站根目录下,通过https://your-site.com/llms.txt访问。下面是一个完整的示例,我逐段解释每个部分的作用:
# 你的网站名称 > 一句话描述你的网站是做什么的,面向什么人群。 这里是更详细的介绍,可以写两三句话,说明网站的核心价值、内容特色、更新频率等。这段文字会帮助AI快速建立对你网站的认知。 ## 核心内容 - [文章标题一](https://your-site.com/article-1):这篇文章讲了什么,用一句话概括。 - [文章标题二](https://your-site.com/article-2):这篇文章的核心结论是什么。 - [文章标题三](https://your-site.com/article-3):适合什么场景下参考。 ## 文档与教程 - [入门指南](https://your-site.com/guide):从零开始的完整教程。 - [API文档](https://your-site.com/api):接口说明和调用示例。 ## 可选内容 - [关于我们](https://your-site.com/about):团队背景和联系方式。几个关键点需要注意。第一行的# 网站名称是必须的,它相当于文件的标题。第二行的> 描述用引用块格式,是一句话简介,AI会优先读这一行来理解你的网站定位。接下来的正文段落可以补充更多背景信息。
## 核心内容这个章节是最重要的,它用列表形式列出了你希望AI优先抓取和引用的页面。每个条目的格式是[标题](URL):摘要,摘要要写得具体,不要写“这是一篇好文章”这种废话,而要写清楚这篇文章解决了什么问题、核心结论是什么。AI在判断是否引用时,会重点看这个摘要。
## 可选内容章节里的链接,AI会视为次要内容,抓取优先级较低。你可以把关于页、联系方式、版权声明等放在这里。
3.3 把llms.txt放到服务器上并验证生效
写好llms.txt之后,把它放到网站的根目录。如果你用的是Nginx,确保根目录的静态文件能被正常访问。如果你用的是某种CMS或框架,可能需要配置一条路由规则,让/llms.txt返回这个文件的内容。
验证方法很简单,直接在浏览器访问https://your-site.com/llms.txt,看是否能正常显示。然后用curl确认返回的Content-Type:
curl -I https://your-site.com/llms.txt理想情况下,Content-Type应该是text/plain或text/markdown。如果返回的是text/html,说明你的服务器可能把它当成了普通网页处理,虽然内容能读到,但语义上不够清晰。可以在Nginx配置里加一条:
location = /llms.txt { default_type text/plain; alias /path/to/your/llms.txt; }改完配置记得reload Nginx。这一步看起来不起眼,但能确保AI爬虫以正确的方式解析你的文件。
还有一个实操心得:llms.txt不是写一次就完事的。每次你发布了重要的新内容,都应该同步更新这个文件,把新页面加到## 核心内容列表里。我自己的做法是把这个更新动作写进了发布流程的检查清单,和提交sitemap放在同一步骤里。坚持了几个月之后,明显能感觉到AI搜索对我新发布内容的抓取速度变快了。
4. 结构化数据:让AI一眼看懂你的内容在讲什么
4.1 为什么纯文本内容在AI搜索里吃亏
AI搜索的解析器在处理一个页面时,面临的核心难题是:它需要从一堆HTML标签、导航链接、广告脚本、样式代码里,准确提取出“这个页面的主题是什么”“核心内容在哪”“哪些信息是可信的”。如果你的页面只有纯文本,没有任何结构化标注,解析器就只能靠猜。猜对了,你的内容被引用;猜错了,你的内容就被当成噪声过滤掉。
结构化数据的作用,就是用一套AI和搜索引擎都能理解的“通用语言”,明确告诉解析器:这是一篇文章,标题是什么,作者是谁,发布时间是什么,正文在哪,核心要点有哪些。这套语言目前最通用的格式是JSON-LD,它以<script type="application/ld+json">的形式嵌入在HTML中,不影响页面显示,但能被爬虫精准读取。
很多人觉得结构化数据是“锦上添花”,做了没坏处但不做也行。但在AI搜索场景下,它的重要性被放大了。因为AI在筛选引用来源时,会优先选择那些“信息明确、结构清晰”的页面。一个标注了Article、FAQPage、HowTo等schema的页面,在AI眼里就是“这个页面知道自己是什么,信息可信度高”,引用优先级自然更高。
4.2 Article与FAQPage标注的代码模板
下面给出两个最常用的JSON-LD模板,你可以直接复制修改。第一个是Article类型,适合博客文章、教程、新闻等内容:
<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Article", "headline": "你的文章标题", "description": "文章的一句话摘要,控制在150字以内", "author": { "@type": "Person", "name": "作者名" }, "datePublished": "2025-01-15", "dateModified": "2025-01-20", "mainEntityOfPage": { "@type": "WebPage", "@id": "https://your-site.com/your-article" }, "publisher": { "@type": "Organization", "name": "你的网站名称" } } </script>第二个是FAQPage类型,适合问答形式的页面。这个类型对AI搜索特别友好,因为AI生成回答时经常需要引用问答对:
<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [ { "@type": "Question", "name": "GEO和SEO有什么区别?", "acceptedAnswer": { "@type": "Answer", "text": "GEO关注的是让AI在生成回答时引用你的内容,SEO关注的是在传统搜索结果中获得排名。两者的优化手段有交集,但评判标准不同。" } }, { "@type": "Question", "name": "llms.txt是必须的吗?", "acceptedAnswer": { "@type": "Answer", "text": "目前不是强制标准,但配置llms.txt能帮助AI搜索更快理解你的网站结构,提升内容被引用的概率。" } } ] } </script>把这段代码放到页面的<head>或<body>里都可以,JSON-LD的位置不影响解析。但要注意,headline和description必须和页面实际内容一致,不要为了优化而写虚假信息,AI搜索有交叉验证机制,标注和实际内容不符反而会降低信任度。
4.3 用Python批量检查页面结构化数据是否合规
如果你有几十上百个页面,手动检查结构化数据是不现实的。写个简单的Python脚本批量跑一遍,效率会高很多。下面这个脚本用requests和beautifulsoup4提取每个页面的JSON-LD,并检查必填字段:
import json import requests from bs4 import BeautifulSoup def check_structured_data(url): try: resp = requests.get(url, timeout=10, headers={ "User-Agent": "Mozilla/5.0 (compatible; AISearchBot/1.0)" }) soup = BeautifulSoup(resp.text, "html.parser") scripts = soup.find_all("script", type="application/ld+json") if not scripts: return {"url": url, "status": "missing", "detail": "未找到JSON-LD"} results = [] for script in scripts: try: data = json.loads(script.string) schema_type = data.get("@type", "unknown") has_headline = "headline" in data or "name" in data has_desc = "description" in data results.append({ "type": schema_type, "has_title": has_headline, "has_description": has_desc }) except json.JSONDecodeError: results.append({"type": "invalid", "error": "JSON解析失败"}) return {"url": url, "status": "ok", "schemas": results} except Exception as e: return {"url": url, "status": "error", "detail": str(e)} # 批量检查 urls = [ "https://your-site.com/article-1", "https://your-site.com/article-2", ] for url in urls: result = check_structured_data(url) print(json.dumps(result, ensure_ascii=False, indent=2))这个脚本会输出每个页面的结构化数据状态:有没有JSON-LD、是什么类型、标题和描述字段是否齐全。跑一遍之后,你就能快速定位哪些页面需要补标注。我建议把这个检查加到你的发布流程里,每次新文章上线后自动跑一次,避免遗漏。
5. 内容分块与可引用性:AI到底会摘走你哪一段
5.1 AI引用内容的粒度:它要的是“片段”不是“全文”
这是很多人做GEO时最大的认知误区:以为把文章写好、写长、写全,AI就会引用。实际上,AI搜索在生成回答时,引用的是与问题最匹配的片段,而不是整篇文章。它可能只摘走你文章里的一个段落、一个列表、甚至一句话。所以,你的内容是否“可被切分、可被定位、可被独立理解”,直接决定了它能不能被引用。
这就引出一个关键概念:内容分块。你需要有意识地把文章组织成一个个语义完整的小块,每个块都能独立回答一个小问题。这样AI在检索时,能精准命中某个块,并把它作为引用来源。如果你的文章是一大段没有小标题、没有列表、没有明确段落划分的“文字墙”,AI很难从中切出可用的片段,引用你的概率就会大幅降低。
具体怎么做?几个实操原则。第一,每个H2或H3小节尽量聚焦一个具体问题,小节标题本身就是这个问题的概括。第二,在关键结论处使用加粗或引用块,给AI一个“这里是重点”的信号。第三,多用列表和表格来组织对比性、步骤性信息,这些结构天然适合被切分和引用。第四,每个段落控制在合理长度,避免一段话超过五六行。
5.2 用标题层级和语义标签给AI画“引用地图”
HTML的标题层级(h1到h6)不只是给读者看的,它也是AI解析页面结构的重要依据。一个结构清晰的页面,AI能快速建立“目录”,知道哪个部分讲什么,从而在需要时精准定位。
检查一下你的页面:是不是只有一个h1?h2和h3的层级是否合理?有没有为了视觉效果而滥用标题标签(比如把普通文字加粗当成标题用)?这些细节都会影响AI对你内容结构的理解。
除了标题层级,语义化标签也很重要。<article>、<section>、<aside>、<nav>这些标签能帮助AI区分正文和辅助内容。如果你的正文全部包在<div>里,AI需要额外推断哪部分是正文,增加了出错概率。把正文用<article>包起来,每个小节用<section>包起来,是一个低成本高回报的改动。
还有一个技巧:在关键段落前加上一个简短的“摘要句”,用<p>标签明确标出。比如在讲某个技术方案之前,先用一句话概括“这个方案的核心是用X解决Y问题”,然后再展开细节。这样AI在切分内容时,能把这个摘要句作为该块的“标签”,更容易匹配到相关问题。
5.3 避免把关键信息藏在图片和交互组件里
这一条是很多技术博客和产品文档的通病。你的架构图很漂亮,你的对比表格做成了图片,你的代码示例放在可折叠的代码块里需要点击才展开——在人类读者看来体验很好,但在AI爬虫看来,这些信息等于不存在。
AI爬虫对图片内容的识别能力有限,虽然有些高级爬虫能通过OCR提取图片文字,但准确率和覆盖率远不如直接读取HTML文本。所以,凡是关键信息,一定要有文本形式的呈现。架构图可以配一段文字说明,对比表格尽量用HTML的<table>标签而不是图片,代码示例默认展开而不是折叠。
对于交互组件,比如标签页、手风琴折叠面板,AI爬虫通常只能看到默认展开的内容。如果你的核心内容藏在第二个标签页里,那它很可能抓不到。解决方案是确保默认状态下关键内容可见,或者为每个标签页提供独立的URL锚点,让爬虫能直接访问。
我自己的做法是:在发布前用“纯文本视角”过一遍文章。把页面上的图片、样式、交互全部去掉,只看纯文本,问自己“如果我只读到这些文字,能不能理解文章的核心内容?”如果答案是否定的,说明有太多信息依赖非文本形式呈现,需要补文本说明。
6. 抓取预算与服务器响应:别让技术细节拖了后腿
6.1 服务器响应速度对AI爬虫抓取频率的影响
AI爬虫和传统搜索引擎爬虫一样,对服务器响应速度有要求。如果你的服务器响应慢,爬虫会降低抓取频率,甚至放弃抓取。这不是危言耸听,而是一个很实际的工程约束:爬虫的资源是有限的,它会优先抓取那些响应快、内容质量高的站点。
怎么判断你的服务器响应速度是否达标?用curl的-w参数可以测量各个阶段的时间:
curl -o /dev/null -s -w "DNS解析: %{time_namelookup}s\n建立连接: %{time_connect}s\n首字节: %{time_starttransfer}s\n总耗时: %{time_total}s\n" https://your-site.com/your-page重点关注time_starttransfer(首字节时间,TTFB)。一般来说,TTFB控制在200毫秒以内是比较理想的,超过500毫秒就偏慢了,超过1秒会明显影响爬虫的抓取意愿。如果你的TTFB偏高,排查方向包括:服务器配置是否够用、有没有启用缓存、数据库查询是否过慢、有没有使用CDN加速静态资源。
6.2 用日志分析找出AI爬虫的真实抓取行为
与其猜测AI爬虫有没有来、来了抓了什么,不如直接看服务器日志。Nginx的access log里记录了每个请求的UA、URL、响应码和时间。你可以用grep快速筛选出AI爬虫的请求:
grep -iE "GPTBot|ClaudeBot|PerplexityBot|AISearchBot" /var/log/nginx/access.log | tail -50这条命令会列出最近50条AI爬虫的请求记录。观察几个点:它们抓了哪些页面?响应码是200还是404/403?抓取频率如何?如果发现大量403,说明你的服务器或防火墙在拦截它们;如果发现404,说明它们试图抓取的URL不存在,可能是sitemap里有错误链接。
更进一步,你可以统计AI爬虫最常抓取的页面:
grep -iE "GPTBot|ClaudeBot|PerplexityBot" /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20这个统计能告诉你AI爬虫对你网站的哪些内容最感兴趣。如果发现它们反复抓取某些页面但你的核心内容页面却很少被抓,那说明你的内容发现机制有问题,可能需要检查sitemap、内链结构或llms.txt的配置。
6.3 给AI爬虫留一条“快车道”的配置思路
基于日志分析的结果,你可以有针对性地优化。如果发现AI爬虫抓取频率低,可以考虑在服务器层面给它们更友好的响应。比如在Nginx配置里,对AI爬虫的请求设置更宽松的限流规则,或者确保它们不会被WAF(Web应用防火墙)误拦。
一个常见的坑是:很多网站用了Cloudflare等CDN服务,默认的安全策略可能会把AI爬虫当成恶意流量拦截。你需要检查CDN的防火墙规则,确保主流AI爬虫的UA被加入白名单。具体操作因平台而异,但核心思路是:在CDN的Bot Management或Firewall Rules里,为已知的AI爬虫UA创建允许规则。
另一个实操建议是:确保你的sitemap.xml是最新的,并且在robots.txt里声明了sitemap的位置。虽然AI搜索不一定会读sitemap,但这是一个低成本的内容发现渠道,做了没坏处。sitemap里只放你希望被引用的高质量页面,不要把标签页、归档页、分页列表全塞进去,那样反而稀释了核心内容的权重。
7. 我踩过的几个坑和最后想说的
做GEO这段时间,踩过的坑不少,挑几个有代表性的说说。第一个坑是过度依赖llms.txt。我一开始以为写好llms.txt就万事大吉了,结果发现AI爬虫还是会抓取一些我没列进去的页面,而列进去的页面也不一定被优先抓。后来才明白,llms.txt是“建议”不是“命令”,AI搜索有自己的判断逻辑,llms.txt只是辅助信号之一。真正起决定作用的还是内容本身的质量和结构。
第二个坑是忽略了旧内容的更新。我早期的一些文章结构化数据标注不全,内容分块也不够清晰,但一直没去改,觉得“新文章做好就行了”。后来看日志发现,AI爬虫抓取旧文章的频率其实很高,但这些旧文章因为结构问题很少被引用。于是我花了一个周末批量补标注、拆分长段落、加小标题,之后引用率明显有提升。这件事让我意识到,GEO是一个存量优化和增量优化并重的工作,不能只盯着新内容。
第三个坑是robots.txt改完之后忘了验证。有一次我加了一条规则想屏蔽某个目录,结果写错了路径,把整个文章目录都屏蔽了。更麻烦的是,我自己用浏览器访问是正常的(因为浏览器不遵守robots.txt),所以过了好几天才发现。从那以后,我每次改robots.txt都会用curl模拟不同UA验证一遍,确认规则按预期生效。
最后分享一个我觉得最实用的习惯:定期用“AI视角”审视自己的网站。具体做法是,把你希望被引用的核心问题列出来,然后想象自己是一个AI,只能通过抓取你的网站来回答这些问题。你的网站能提供足够清晰、足够结构化的答案吗?如果答案是否定的,那问题就不在AI,而在你的内容组织方式。GEO的本质不是讨好算法,而是让你的内容以AI能理解的方式呈现出来。把这件事做好了,引用是自然的结果。