作为一名天天和代码打交道的程序员,最近我发现网站的流量生态发生了一些微妙的变化。
以前我们做个人博客或技术网站,每天盯着的是 Google 或百度的收录量,研究怎么通过关键词匹配和外链提高 PageRank(网页排名)。
但现在,越来越多的朋友在遇到技术难题时,不再去搜索引擎一页页翻答案,而是直接打开 ChatGPT、Kimi、Perplexity 或者使用 Google 的 AI Overviews(AI 搜索)提问:“Go 语言里的 GMP 模型是怎么回事?”
这时候,AI 会在几秒钟内给你整理出一篇通俗易懂的回答,并且在旁边贴心地标上几个彩色的小气泡(引用来源链接),点开一看,正是某个开发者的技术博客。
这种让 AI 抓取你的内容,并在回答中把你的网站作为“参考书目”引用出来的技术,就是最近火起来的 GEO (Generative Engine Optimization,生成式引擎优化)。
前段时间,我花精力将自己的博客系统做了一次彻底的现代化重构。这篇文章就以我的实际项目(Nuxt 4 + Go/Gin)为例,用大白话加代码的方式,手把手带你看看我是如何落地 SEO 和 GEO 优化的。
1. 为什么大模型能搜到你的网页?通俗聊聊 RAG 机制
在动手改代码之前,我们先花一分钟理解一下 AI 搜索的原理。为什么 AI 能知道你的网站写了什么?
现在的 AI 搜索(如 Perplexity)后面普遍挂载了一个叫 RAG(检索增强生成) 的技术。简单来说,它的工作流程是这样的:
- 用户提问:用户输入“如何用 Docker 部署 Elasticsearch 8”。
- 小秘书搜集资料:AI 搜索引擎先假装成普通用户,用传统搜索引擎去网上把排在前面的几十个网页全部抓下来(这需要你的网页非常利于抓取,不能是动态加载的空壳子)。
- 大模型提炼回答:AI 读完这几十个网页,把里面的核心干货提炼出来,组合成一段通俗的话发给用户。
- 贴上参考链接:为了防止自己胡说八道(幻觉),AI 会把刚刚做出贡献的网页网址贴在旁边,当成“引用源”。
所以,GEO 优化的核心目的,就是降低 AI “搜集和阅读资料”的成本,让它觉得读你的文章最轻松、最靠谱。
2. 核心技术落地:我的四步走改造方案
为了让 AI 觉得我们的网站“真香”,我给自己的博客系统做了以下四步升级。
第一步:扔掉 SPA,拥抱 SSR 与语义化 URL (Slug)
如果你的博客还是纯单页应用(SPA,比如用纯 Vue 或 React 打包出来的静态页面),在 AI 时代基本就宣告出局了。因为 SPA 初始只有一个 <div id="app"></div> 的空壳,内容全靠浏览器运行 JS 动态渲染。AI 爬虫可没空等你慢慢加载 JS,它一进来看到是空的,直接就转头走了。
所以我做的第一件事,就是将博客重构为 Nuxt 4 的服务端渲染(SSR)。这样当爬虫访问时,服务器直接就把渲染好的、带文字内容的完整 HTML 塞给它。
同时,我们还要改造网址(URL)的拼写。
- 以前的网址:
https://yoursite.com/article/32(冷冰冰的数字,爬虫和人类都看不懂) - 现在的网址:
https://yoursite.com/article/32-online-platform-personalized-recommendation(带上了英文关键字,语义清晰)
🛠️ 我在项目里的代码实现:
在前端,我写了一个工具类来自动转换文章链接。如果文章有英文标题,就优先转换英文;如果没有,就把中文特殊字符过滤掉并用中划线 - 连接:
typescript
// utils/slug.ts
/**
* 将中英文标题转换为符合 SEO/GEO 标准的扁平化 URL Slug
* 比如将 "Docker 部署 ES 8.x" 变成 "docker-deployment-es-8x"
*/
export function getSlug(title: string, titleEn?: string): string {
const baseText = titleEn || title;
return baseText
.toLowerCase()
// 过滤掉除了英文字母、数字、中文、空格和中划线以外的杂质符号
.replace(/[^a-z0-9\u4e00-\u9fa5\s-]/g, '')
// 把连在一起的空格全部换成单个中划线
.replace(/\s+/g, '-')
// 防止出现连续的多个中划线,比如 --- 变成 -
.replace(/-+/g, '-')
.trim();
}
/**
* 拼装完整的文章链接
*/
export function getArticleLink(article: { id: number; title: string; title_en?: string }): string {
const slug = getSlug(article.title, article.title_en);
return `/article/${article.id}-${slug}`;
}
在后端(我用的是 Go 的 Gin 框架),路由直接设计成 /article/:id_slug。在解析参数时,其实非常通俗简单——我们只拿最前面的主键 ID 去查数据库,后面那一长串英文直接忽略掉:
go
// 后端 Gin 路由控制器解析参数示例
func GetArticleDetail(c *gin.Context) {
// 比如接收到的是 "32-online-platform-personalized-recommendation"
idSlug := c.Param("id_slug")
// 按照 "-" 分割,拿最前面的 "32" 即可
idStr := strings.Split(idSlug, "-")[0]
id, err := strconv.Atoi(idStr)
if err != nil {
c.JSON(400, gin.H{"error": "无效的文章ID"})
return
}
// 依然使用极速的主键 ID 查询数据库,保证性能
article := queryArticleFromDB(id)
c.JSON(200, gin.H{"code": 0, "data": article})
}
第二步:递上名片——注入 JSON-LD 结构化元数据
大模型爬虫进到你的网页,如果从 HTML 段落里去猜哪段是正文、哪段是作者、什么时候发布的,不仅费劲而且容易猜错。
我们可以主动给它递一张“名片”——JSON-LD。这是把网页的核心信息打包成一段统一规范的 JSON 代码,藏在 HTML 的头部(用户看不见,但爬虫一扫就懂)。
🛠️ 我在项目里的代码实现:
在 Nuxt 4 的文章详情页中,我们利用 useHead 动态拼装符合 Schema.org 标准的 BlogPosting 元数据:
vue
<!-- pages/article/[id].vue -->
<script setup lang="ts">
import { computed } from 'vue'
const { data: article } = await useAsyncData(`article-${id}`, () => fetchArticle(id))
// 动态拼装 AI 最喜欢的 JSON-LD 结构化数据
const jsonLd = computed(() => {
if (!article.value) return null
return {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
'headline': article.value.title,
'description': article.value.abstract,
'image': article.value.banner_path ? [article.value.banner_path] : [],
'datePublished': article.value.created_at,
'author': {
'@type': 'Person',
'name': '山姆叔叔'
},
'publisher': {
'@type': 'Organization',
'name': '山姆的技术博客',
'logo': {
'@type': 'ImageObject',
'url': 'https://yoursite.com/logo.png' // 替换成你真实的 logo 地址
}
}
}
})
// 将其写入 HTML 头部
useHead({
title: () => `${article.value?.title} - 山姆的博客`,
script: [
{
type: 'application/ld+json',
// 💡 注意:必须用 innerHTML 写入,不能写在 children 里,否则 SSR 服务端渲染时字符会被转义报错
innerHTML: () => jsonLd.value ? JSON.stringify(jsonLd.value) : ''
}
]
})
</script>
只要这段代码生效,Google 就会给你的文章在搜索结果里加上缩略图、发布时间和作者头像(Rich Snippet 效果),AI 也能以 100% 的准确率提取到这篇文章的所有属性。
第三步:自动更新网站“户口本”——动态多源 Sitemap
光写好网页还不行,得让爬虫知道你有新文章更新了。这就需要 sitemap.xml(站点地图)。以前很多人是手动写一个 XML 文件,但对于经常更新的博客,这太不现实了。
🛠️ 我在项目里的代码实现:
我集成了 @nuxtjs/sitemap 模块,并写了一个服务端的接口,让它每天或者在有新文章时,自动把最新的 Slug 链接吐出来。
首先在 nuxt.config.ts 里声明 Sitemap 数据源:
typescript
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/sitemap'],
sitemap: {
// 动态文章的 URL 数据源指向我们的服务端路由
sources: ['/api/__sitemap__/urls'],
// 排除掉不需要被收录的后台页面
exclude: ['/login', '/admin/**']
}
})
接着在服务端创建 /server/api/__sitemap__/urls.ts,这个接口只在后台默默运行,通过向 Go 后端发送请求拉取全量文章列表,生成站点地图专用的 URL 数组:
typescript
// server/api/__sitemap__/urls.ts
import { getArticleLink } from '~/utils/slug'
interface ApiResponse {
code: number
data: {
list: Array<{ id: number; title: string; title_en: string; created_at: string }>
}
}
export default defineSitemapEventHandler(async () => {
const config = useRuntimeConfig()
const backendBase = config.serverApiUrl || 'http://127.0.0.1:8000'
try {
// 请求 Go 接口获取全站所有文章
const res: ApiResponse = await $fetch(`${backendBase}/api/articles?page=1&limit=1000`)
if (res.code !== 0) return []
// 把文章列表转换为 sitemap 需要的格式
return res.data.list.map(article => ({
// 这里调用的依然是第一步里写的 getArticleLink 工具类,保证两边 URL 完全统一
loc: getArticleLink({
id: article.id,
title: article.title,
title_en: article.title_en,
}),
lastmod: article.created_at,
changefreq: 'weekly' as const, // 告知爬虫该页面大概每周更新一次
priority: 0.9 // 权重分:0.0 ~ 1.0
}))
} catch (e) {
console.error('Sitemap 接口获取文章列表失败:', e)
return []
}
})
做完这步,当我们访问 http://yoursite.com/sitemap.xml 时,就能看到一个清爽的、实时的全站链接地图了,AI 爬虫能顺着这本“户口本”把我们全站扫得一清二楚。
第四步:重新整理 Robots 规则,欢迎 AI 爬虫
有些传统的 Robots.txt 屏蔽了太多路径。我们要在其中明确写出允许哪些大模型爬虫抓取,这也是 GEO 优化里最简单但最重要的一环。
🛠️ 我在项目里的代码实现:
编辑并优化 public/robots.txt,重点向主流 AI 放行:
ini
User-agent: *
Allow: /
Disallow: /login
Disallow: /admin
# ---- OpenAI / ChatGPT Search 专属绿卡 ----
User-agent: GPTBot
Allow: /
# ---- Google Gemini / 谷歌 AI 搜索 ----
User-agent: Google-Extended
Allow: /
# ---- Anthropic Claude 专属爬虫 ----
User-agent: ClaudeBot
Allow: /
# ---- Perplexity AI 搜索爬虫 ----
User-agent: PerplexityBot
Allow: /
# 告诉它们站点地图在哪里
Sitemap: https://yoursite.com/sitemap.xml
3. 如何验证我们做的是否有效?
改造上线后,你可以通过以下两个手段来测试效果:
- 测试 JSON-LD 格式:使用 Google 官方的 富媒体结果测试工具 输入你的文章链接,只要显示“未发现错误”并且识别出完整的
BlogPosting架构,说明你的元数据成功了。 - 测试 AI 引用:尝试在 Perplexity 里用自然语言搜索你文章所讲解的技术难点。你会发现,AI 在整理回答时,会将你的文章作为小气泡卡片展示在答案附近,链接直接指向我们拼写优美的 Slug URL。
4. 总结与建议
在人工智能重塑互联网的今天,“内容为王”依然是不变的真理,但“如何让 AI 更好地阅读内容”成为了程序员必备的硬核技能。
GEO 的落地,本质上就是减少信息在传输、抓取、理解过程中的损耗。通过 服务端渲染(SSR)确保内容立即可见,通过 JSON-LD 给大模型吃定心丸,通过 语义化 URL 和动态 Sitemap 方便收录。
如果你也在搭建或者升级你的个人技术博客、内容型平台,建议把这几项列入你的重构清单中。技术不应该只是自嗨,能在这个 AI 时代抢占流量入口,让大模型帮我们引流,才是真正的“技术变现”第一步。