构建 GEO 检测引擎:GetCiteFlow 如何用混合分析架构帮网站被 AI 引用

构建 GEO 检测引擎:GetCiteFlow 如何用混合分析架构帮网站被 AI 引用

> **一句话总结:** 传统 SEO 在 AI 搜索时代已经不够用了。本文从技术角度深度拆解什么是 GEO(生成式引擎优化),以及我们如何用 Next.js 15 + Gemini 构建了一个能检测并修复网站 AI 可见性的平台。

> **发布说明:** 掘金/CSDN/SegmentFault 在编辑器页面设置标签和封面图即可,本文不含 frontmatter 专有字段。知乎直接粘贴正文,标题和话题在编辑器设置。


你有没有发现,现在越来越多人不再"谷歌一下",而是直接问 ChatGPT、Perplexity、Claude 或者豆包。这个转变对内容发现的影响是革命性的------但绝大多数站长还没意识到问题所在。

传统 SEO 的指标(外链、域名权重、关键词密度)与 AI 引用率之间只有大约 **0.3 的相关性**。你在 Google 排第一,不代表 ChatGPT 会提到你。这就是 **GEO(Generative Engine Optimization,生成式引擎优化)** 要解决的问题。

这篇文章我会从技术实现角度,带你走进 **GetCiteFlow**------我们构建的 GEO 分析平台------包括架构设计、关键代码和踩过的坑。


1. 什么是 GEO?和 SEO 有什么本质区别?

当 ChatGPT 或 Claude 回答用户问题时,它并不是像 Google 那样"排名"。它更关注那些**容易被引用、总结和归因**的信号。

通过分析数千个网站,我们总结出六个关键维度:

| 维度 | 说明 |

|---|---|

| **AI 可见性** | AI 能否找到并解析你的内容? |

| **FAQ 覆盖率** | 是否有结构化的 FAQ Schema? |

| **实体清晰度** | 页面是否明确说明了"自己是什么"? |

| **权威性** | 有没有原创研究或署名作者? |

| **内容结构** | 是否使用了列表、表格、清晰的标题层级? |

| **摘要优化** | 页面开头是否有 AI 可提取的清晰摘要? |

核心洞察:**AI 搜索引擎不以人类的方式"阅读"页面。** 它们寻找的是机器可读的信号------结构化数据、实体定义、`llms.txt` 文件------而非关键词密度。


2. 混合分析架构:AI 大模型 + 确定性信号检测

GetCiteFlow 采用**混合分析架构**。我们不依赖 LLM 单方面评价(它会胡编乱造),而是结合两层独立的分析:

```

用户输入 URL

|

v

1 爬取网站 → 提取信号(HTML 解析)

|

v

2 格式化信号 → 发送给 AI(Gemini / OpenAI / Deepseek)

|

v

3 AI 返回结构化 JSON(分数、细分、建议)

|

v

4 合并确定性检测结果(列表、meta 长度等)

|

v

5 缓存结果 + 渲染报告

```

核心编排函数出自 `lib/analyze.ts`:

```typescript

export async function analyzeSite(url: string): Promise<Record<string, unknown>> {

const cacheKey = `report:${url}`;

const cached = cacheGet<Record<string, unknown>>(cacheKey);

if (cached) return { ...cached, cached: true };

// 去重:同一 URL 的并发请求只执行一次

if (pendingCache.has(cacheKey)) {

return pendingCache.get(cacheKey)!;

}

const analyze = async () => {

const activeProvider = getProvider();

const fn = providerFnsactiveProvider;

const report = await fn(url);

// 合并 AI 结果 + 确定性信号检测

const siteData = await getSiteData(url);

const deterministicMissing = getDeterministicMissing(siteData);

const aiMissing = (report.missing as string\[\]) || \[\];

const mergedMissing = ...new Set(\[...aiMissing, ...deterministicMissing)];

const result = { ...report, missing: mergedMissing };

cacheSet(cacheKey, result, CACHE_TTL_MS);

return result;

};

const promise = analyze();

pendingCache.set(cacheKey, promise);

return promise;

}

```

为什么要混合?**LLM 擅长定性判断但不擅长"数数"。** AI 可能漏掉一个页面没有任何 `<ul>` 标签这个事实,但简单的正则检查不会。两者结合,各取所长。


3. 爬虫模块:用正则提取确定性信号

爬虫(`lib/scrape.ts`)是一个纯 HTTP 抓取器------不需要无头浏览器。它获取 HTML,用正则解析结构化信号,并检查关键静态文件。

```typescript

async function extractFromHtml(html: string) {

const titleMatch = /<title\^\>*>(\^\<*)<\/title>/i.exec(html);

const hasOpenGraph =

/<meta\^\>+property="'og:(title|description|image)"'/i.test(html);

const hasFaqSchema = /* 解析 JSON-LD <script> 块 */;

const hasOrderedLists = /<ol\\s\>/i.test(html);

const avgParagraphLength = /* 通过 <p> 标签计算 */;

const hasSummarySection =

/\b(key takeaways?|executive summary|tldr|tl;dr)\b/i.test(bodyLower);

return { title, hasOpenGraph, hasFaqSchema, hasOrderedLists, ... };

}

```

三个关键文件的检查是并行进行的:

```typescript

const hasRobotsTxt, hasSitemap, hasLlmstxt = await Promise.all([

checkStaticFile(resolvedOrigin, "/robots.txt"),

checkStaticFile(resolvedOrigin, "/sitemap.xml"),

checkStaticFile(resolvedOrigin, "/llms.txt"),

]);

```

`llms.txt` 的检测尤其重要------这是一个相对较新的标准(由 llmstxt 社区提出),专门为 AI 爬虫创建机器可读的站点索引。拥有 `llms.txt` 文件的网站,AI 引用率显著更高。


4. AI 分析层:用 Gemini 的结构化输出

AI 分析部分,我们使用 Google Gemini 原生支持的结构化输出。这非常关键------没有它,从 LLM 解析自由格式的 JSON 脆弱且容易出错。

```typescript

async function analyzeWithGemini(url: string) {

const siteData = formatSiteData(url, await getSiteData(url));

const response = await getAiClient().models.generateContent({

model: "gemini-3-flash-preview",

contents: ANALYZE_PROMPT(url, siteData),

config: {

temperature: 0, // 确定性输出

responseMimeType: "application/json",

responseSchema: {

type: Type.OBJECT,

required: "score", "breakdown", "missing", "suggestions", "summary",

properties: {

score: { type: Type.NUMBER },

breakdown: {

type: Type.OBJECT,

properties: {

aiVisibility: { type: Type.NUMBER },

faqCoverage: { type: Type.NUMBER },

entityClarity: { type: Type.NUMBER },

authority: { type: Type.NUMBER },

contentStructure: { type: Type.NUMBER },

summaryOptimization: { type: Type.NUMBER },

},

},

missing: { type: Type.ARRAY, items: { type: Type.STRING } },

suggestions: { type: Type.ARRAY, items: { type: Type.STRING } },

summary: { type: Type.STRING },

},

},

},

});

return JSON.parse(response.text || "{}");

}

```

这里的关键设计决策:

  • **`temperature: 0`**------我们需要确定性的、可复现的结果。不需要创造力。

  • **`responseSchema`**------告诉 Gemini 确切需要什么 JSON 形状。没有这个,你会得到解析错误、字段缺失和类型不一致。

  • 我们将**实际爬取的信号**注入到提示词中。AI 不是瞎猜------它基于我们找到的数据进行评估。

提示词模板(`lib/ai-provider.ts`):

```typescript

export const ANALYZE_PROMPT = (url: string, siteData?: string) =>

`分析该网站的 AI 可见性(GEO):${url}

{siteData ? \`以下是网站的实际检测信号:\\n{siteData}\n\n请基于这些真实信号进行分析,不要猜测。` : ''}

根据上述信号具体评估以下因素:

  • contentStructure (0-100):内容结构对 AI 解析的友好程度...

  • summaryOptimization (0-100):页面为 AI 摘要所做的优化程度...

仅返回具有以下精确键的 JSON 对象:

{ "score": <数字 0-100>, "breakdown": { ... }, "missing": ..., "suggestions": ..., "summary": "..." }`;

```

我们还支持 OpenAI 和 Deepseek 作为备用提供商,通过 `AI_PROVIDER_DEFAULT` 环境变量切换。架构设计使得添加新提供商非常简单------只需实现相同的函数签名。


5. 缓存策略:内存缓存 + 请求去重

每次分析都要调用 LLM API(成本高)并爬取网站(速度慢),因此缓存至关重要。我们使用简单的内存 `Map`,TTL 为 1 小时。

```typescript

interface CacheEntry<T> {

data: T;

expiresAt: number;

}

const store = new Map<string, CacheEntry<unknown>>();

const CLEAN_INTERVAL = 60_000;

let lastClean = 0;

function clean() {

const now = Date.now();

if (now - lastClean < CLEAN_INTERVAL) return;

lastClean = now;

for (const key, entry of store) {

if (now > entry.expiresAt) store.delete(key);

}

}

export function cacheGet<T>(key: string): T | null {

clean();

const entry = store.get(key);

if (!entry || Date.now() > entry.expiresAt) return null;

return entry.data as T;

}

export function cacheSet<T>(key: string, data: T, ttlMs: number): void {

store.set(key, { data, expiresAt: Date.now() + ttlMs });

}

```

我们还使用 **pending cache**(`analyze.ts` 中的 `pendingCache`)来去重同一 URL 的并发请求------如果两个用户同时提交相同的 URL,只执行一次分析:

```typescript

const pendingCache = new Map<string, Promise<Record<string, unknown>>>();

// ...

if (pendingCache.has(cacheKey)) {

return pendingCache.get(cacheKey)!; // 等待正在进行的请求

}

```

对于生产环境,你肯定需要 Redis 或其他分布式缓存。对于单实例部署(如 Vercel 的 serverless functions 并发场景),这种内存方案够用。


6. 限流:优雅降级

我们使用 Upstash Redis 实现滑动窗口限流。一个关键的设计选择:**当 Redis 不可用时,开放通过,而不是拒绝所有请求。**

```typescript

let ratelimit: Ratelimit | null = null;

try {

const redis = Redis.fromEnv();

ratelimit = new Ratelimit({

redis,

limiter: Ratelimit.slidingWindow(max, "1 h"),

analytics: true,

prefix: "@citeflow/ratelimit",

});

} catch {

// Redis 初始化失败------跳过限流

}

export async function checkRateLimit(ip: string): Promise<RateLimitResult> {

if (!ratelimit) {

return { success: true }; // Redis 挂了时允许请求

}

try {

const { success } = await ratelimit.limit(ip);

return success

? { success: true }

: { success: false, reason: 'rate_limited' };

} catch {

return { success: false, reason: 'redis_unavailable' };

}

}

```

为什么选择开放通过?因为免费工具应该是可访问的。因为 Redis 临时故障而阻止所有用户,比少数请求绕过限流更糟糕。


7. 报告页面:SSR + Edge OG 图片

报告页面位于 `app/report/domain/page.tsx`,使用 **SSR(服务端渲染)**,设置了 `maxDuration: 60`(Vercel Pro 计划的超时限制)。这是必要的,因为:

  1. 需要爬取目标网站(网络 I/O)

  2. 需要调用 Gemini/OpenAI(API 延迟)

  3. 渲染前需要完整数据

```typescript

export const maxDuration = 60;

export default async function ReportPage({ params }) {

const { domain } = await params;

const ip = getClientIp(headers());

const result = await getReport(domain, ip);

if (!result.ok) {

// 渲染错误状态:rate_limited, timeout, failed

return <ErrorState reason={result.reason} />;

}

return <ReportView data={result.data} />;

}

```

我们还使用 Edge runtime 为每个报告生成动态 OG 图片:

```typescript

// app/api/og/route.tsx --- 运行在 Vercel Edge

export const runtime = 'edge';

export const dynamic = 'force-dynamic';

```

这意味着每个报告页面都有一个独特的社交预览,显示域名和分数------这对于在 X/Twitter 和 LinkedIn 上分享至关重要。


8. 踩坑总结

8.1 AI 幻觉真实存在------必须给数据做锚定

AI 分析的第一个版本没有将真实爬取信号注入提示词。AI 编造听起来合理但完全错误的评估。**始终在提示词中提供真实数据,并指示模型基于该数据进行分析。**

8.2 结构化输出 > JSON 提示

在使用 `responseSchema` 之前,我们在提示词中使用 `"output valid JSON only"`。成功率大约 70%。使用结构化输出后,接近 99.9%。**只要你的 AI 提供商支持,就用原生结构化输出。**

8.3 积极缓存 + 并发去重

LLM API 调用成本高(每百万 token 约 0.15-3.00)且速度慢(2-5 秒)。内存缓存加上请求去重,消除了所有冗余调用。对于 Vercel 的多并发部署,`pendingCache` 模式至关重要。

8.4 确定性检测能捕获 AI 遗漏的问题

AI 经常遗漏简单的事情,比如"页面上没有列表"或"meta description 太短"。这些用正则检测轻而易举,但 LLM 容易忽略。**混合方案两者都能捕获。**

8.5 GEO 仍处于早期阶段------标准在快速演进

`llms.txt` 是一个提议标准,不是 W3C 规范。AI 搜索中 FAQ Schema 的行为每个月都在变化。构建 GEO 工具意味着随着生态系统的演进而不断迭代。我们将信号检测设计为可插拔层,可以独立于 AI 分析进行更新。


9. 试试看

如果你想检查自己网站的 AI 可见性,前往 getciteflow.ai(https://www.getciteflow.ai) 输入你的网址即可。你会得到一个 0-100 的分数以及具体的、可操作的建议。

整个平台的技术栈:

  • **Next.js 15**(App Router、SSR、Edge Functions)

  • **React 19** + Tailwind CSS 4 + shadcn/ui

  • **Google Gemini** 用于 AI 分析(OpenAI/Deepseek 作为备用)

  • **Upstash Redis** 用于限流

  • **部署在 Vercel**,使用 standalone 输出


*GEO 现在就像是 1998 年的 SEO------还处在非常早期的阶段。今天就开始为 AI 搜索优化的网站,将在 AI 助手成为信息发现主要入口的明天获得复利优势。*

*如果你也在构建这方面的东西,或者对架构有疑问,欢迎在评论区留言交流。*


关于作者:GetCiteFlow 高级工程师,专注于 AI 搜索可见性与生成式引擎优化。

相关推荐
企服AI产品测评局5 小时前
AI Agent实测:Agent Store现成应用如何重塑企业自动化?
运维·人工智能·ai·chatgpt·自动化
LaughingZhu6 小时前
Product Hunt 每日热榜 | 2026-05-31
前端·人工智能·经验分享·搜索引擎·chatgpt·html
superantwmhsxx7 小时前
ChatGPT Images 2.0 角色一致性实战:如何在多轮对话中保持人物形象统一
人工智能·chatgpt
刘大猫.16 小时前
智造短剧新引擎:火山引擎上线「火山剧创 1.0」,制作效率提升 80%
人工智能·ai·chatgpt·机器人·大模型·火山引擎·短剧新引擎
云天AI实战派1 天前
AI 智能体总是跑偏怎么办?ChatGPT/API/Agent 故障排查指南与全流程修复手册
大数据·人工智能·chatgpt·agent
码农小旋风2 天前
使用 ChatGPT 聚合站前,先看安全和隐私判断清单
人工智能·安全·自然语言处理·chatgpt·claude
马丁路的King2 天前
Codex 支持 Windows 远程了
chatgpt
人月神话-Lee2 天前
【图像处理】Core Image 与 GPU 渲染管线——让滤镜飞起来
图像处理·人工智能·ios·chatgpt·ai编程·swift·gpu
程序大视界2 天前
2026年AI大模型三足鼎立:ChatGPT、Claude、Gemini终极对比与选型指南
人工智能·chatgpt