生成式引擎优化(GEO)实战:用Python搭建一套AI搜索可见性追踪系统
一、背景:当你的流量入口变成了AI的“嘴”
如果你运营着一个内容站点,大概率已经发现了一个令人不安的趋势:搜索引擎的“蓝色链接”点击率在下降,而ChatGPT、Perplexity、Kimi等AI工具的“生成式答案”正在吃掉越来越多的用户查询。用户得到了答案,却可能从来没访问过你的网站。
这就是GEO(生成式引擎优化)要解决的核心问题:让你的内容被AI答案引用,并带上可点击的信源链接。
但这里有一个所有SEO从业者都会遇到的困境——传统SEO我们可以用SEMrush/Ahrefs/Search Console来监控关键词排名和点击量,而在AI搜索的世界里,你的内容有没有被引用?被引用的频率是多少?在什么语境下被引用?竞争对手有没有抢走本该属于你的引用位? 这些问题目前几乎没有现成的商业工具能完美回答。
因此,对于真正想深耕GEO的开发者或技术团队来说,自己动手搭建一套AI搜索可见性追踪系统,可能是当前阶段最务实的做法。本文将手把手带你用Python实现一个最小可用的原型。
二、系统目标与架构设计
我们要做的系统,核心功能可以概括为一句话:针对你关心的关键词,定期向主流AI搜索引擎发送查询,解析返回结果中是否引用了你的域名,记录并追踪趋势。
最小化架构包含三个模块:
-
查询调度器:管理关键词列表,定时向AI搜索API发起请求。
-
引用解析器:从API返回结果中提取引用来源,判断是否命中目标域名。
-
存储与可视化:将每次查询的结果存入数据库,生成可见性趋势图。
技术选型上:
-
API选择:Perplexity AI 提供公开API,返回结构中包含引用来源URL,最适合做监控;同时可扩展接入 Google Gemini(通过 Google AI Studio)来追踪 Google AI Overviews 的引用情况(需开启 grounding 功能)。
-
Python库:
requests处理 API 调用,sqlite3轻量存储,matplotlib或 Streamlit 做可视化。 -
部署:可按需运行脚本,或用
cron/ GitHub Actions 定时触发。
三、第一步:调用Perplexity API获取结构化引用
Perplexity的API返回格式非常清晰:答案文本中内嵌了引用编号,而答案末尾的 citations 字段直接列出了所有引用URL。这为我们省去了解析自然文本去提取网址的麻烦。
首先,你需要去 Perplexity 官网申请 API Key。
以下是一个基础调用示例:
python
import requests
import json
API_KEY = "your-perplexity-api-key"
API_URL = "https://api.perplexity.ai/chat/completions"
def query_perplexity(prompt: str, model: str = "sonar-pro") -> dict:
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": model,
"messages": [
{"role": "system", "content": "You are a helpful assistant. Always include citations."},
{"role": "user", "content": prompt}
],
"max_tokens": 1024,
"temperature": 0.2, # 低温确保引用稳定
"return_images": False,
"search_recency_filter": "month" # 可控制时效性
}
response = requests.post(API_URL, headers=headers, json=payload)
response.raise_for_status()
return response.json()
# 测试查询
result = query_perplexity("2025年最值得买的无线降噪耳机推荐")
answer = result["choices"][0]["message"]["content"]
citations = result.get("citations", [])
print("AI答案片段:", answer[:200])
print("引用列表:", citations)
注意参数调优:
-
temperature设为较低值(0.1-0.3)以保证引用结果的相对稳定性,避免同一个问题两次返回截然不同的来源。 -
search_recency_filter可以根据业务需求设为"day"、"week"、"month"或省略,影响AI抓取内容的时效范围。 -
模型选择
sonar-pro比基础版有更丰富的引用和更长的上下文处理能力。
四、第二步:引用解析与域名匹配
拿到引用列表后,我们需要从中识别出“自己的域名”是否出现,以及出现的位序、次数等。
简单实现如下:
python
from urllib.parse import urlparse
TARGET_DOMAINS = ["mysite.com", "myblog.cn"] # 你要监控的域名列表
def analyze_citations(citations: list[str]) -> dict:
analysis = {
"total_citations": len(citations),
"matched_domains": [],
"match_positions": [] # 第一个匹配的引用排在第几位
}
for idx, url in enumerate(citations):
parsed = urlparse(url)
domain = parsed.netloc.lower().lstrip("www.")
if domain in TARGET_DOMAINS:
analysis["matched_domains"].append(domain)
analysis["match_positions"].append(idx + 1) # 转换为人类可读的第几位
analysis["is_any_match"] = len(analysis["matched_domains"]) > 0
return analysis
# 接上一段
citations = result.get("citations", [])
analysis = analyze_citations(citations)
print(analysis)
对于更精细的分析,可以进一步提取引用片段中的锚文本(即AI在答案中用来链接的那几个词),但这需要解析答案文本中的引用标记(如 [1]、[2])并匹配到对应 URL。Perplexity 的答案文本内嵌格式通常是 [1] 等编号,编写正则即可提取上下文。这里留作进阶扩展。
五、第三步:存储到SQLite并构建趋势
单次查询不够,我们要让系统定时运行,把每次的快照记录下来,形成可见性趋势。
建表SQL:
sql
CREATE TABLE IF NOT EXISTS citation_checks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
keyword TEXT NOT NULL,
check_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
total_citations INTEGER,
matched BOOLEAN,
matched_domain TEXT,
match_position INTEGER,
full_citations TEXT, -- JSON格式存储完整引用列表
answer_snippet TEXT
);
完整的主监控函数:
python
import sqlite3
from datetime import datetime
DB_PATH = "geo_monitor.db"
def init_db():
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute('''
CREATE TABLE IF NOT EXISTS citation_checks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
keyword TEXT NOT NULL,
check_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
total_citations INTEGER,
matched BOOLEAN,
matched_domain TEXT,
match_position INTEGER,
full_citations TEXT,
answer_snippet TEXT
)
''')
conn.commit()
conn.close()
def save_check(keyword: str, analysis: dict, citations: list, snippet: str):
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute('''
INSERT INTO citation_checks (keyword, check_time, total_citations, matched,
matched_domain, match_position, full_citations, answer_snippet)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (
keyword,
datetime.now().isoformat(),
analysis["total_citations"],
analysis["is_any_match"],
analysis["matched_domains"][0] if analysis["matched_domains"] else None,
analysis["match_positions"][0] if analysis["match_positions"] else None,
json.dumps(citations),
snippet[:500]
))
conn.commit()
conn.close()
def run_keyword_check(keyword: str):
result = query_perplexity(keyword)
answer = result["choices"][0]["message"]["content"]
citations = result.get("citations", [])
analysis = analyze_citations(citations)
save_check(keyword, analysis, citations, answer)
return analysis
之后只需将你要监控的关键词列表循环跑一遍:
python
KEYWORDS = [
"2025最好的项目管理软件",
"如何选择适合油皮的防晒霜",
"Python异步编程最佳实践"
]
init_db()
for kw in KEYWORDS:
print(f"正在检查关键词: {kw}")
res = run_keyword_check(kw)
status = "✅ 被引用" if res["is_any_match"] else "❌ 未被引用"
print(f" {status},总引用数:{res['total_citations']},匹配位置:{res.get('match_positions')}")
六、第四步:可视化与预警
有了时间序列数据,就可以画出关键域的可见性变化趋势。最简单的实现是用 matplotlib:
python
import matplotlib.pyplot as plt
import sqlite3
import pandas as pd
def plot_visibility_trend(keyword: str):
conn = sqlite3.connect(DB_PATH)
df = pd.read_sql_query(
"SELECT check_time, matched FROM citation_checks WHERE keyword=? ORDER BY check_time",
conn, params=(keyword,)
)
conn.close()
if df.empty:
print("暂无数据")
return
df["check_time"] = pd.to_datetime(df["check_time"])
df["matched_int"] = df["matched"].astype(int)
# 按天聚合,计算当日被引用比例
daily = df.groupby(df["check_time"].dt.date)["matched_int"].mean()
plt.figure(figsize=(10,4))
plt.plot(daily.index, daily.values, marker='o')
plt.axhline(y=1.0, color='green', linestyle='--', alpha=0.5, label='总是被引用')
plt.xticks(rotation=45)
plt.title(f'AI搜索可见性趋势: "{keyword}"')
plt.ylabel('被引用频率 (0~1)')
plt.tight_layout()
plt.show()
更进一步,可以设置预警阈值:当某个核心关键词连续N次检查都未命中时,自动发送邮件或Slack通知,提醒内容团队可能需要更新或重新优化对应页面。
七、扩展与进阶
上述原型只覆盖了最核心的链路,生产级系统还需考虑以下扩展:
1. 多AI引擎覆盖
-
Google Gemini + Grounding:在 Google AI Studio 中调用 Gemini 模型并开启 Google Search grounding,返回结果中会包含
groundingMetadata含引用URL。 -
Kimi / 秘塔等国产AI搜索:目前大多没有公开API,可考虑使用无头浏览器(Playwright)模拟查询并解析DOM,复杂度较高但可行。
2. 引用位置的语义分析
仅仅“被引用”还不够,我们需要知道AI是以正面还是负面语境提及我们?这需要对AI答案片段进行情感分析或语义相似度计算。简单的实现:提取提及你品牌/域名的答案句子,用BERT模型判断其情感倾向。
3. 竞争情报
除了监控自己,还可以加入竞争对手的域名列表,每次查询时一并分析“谁抢到了本属于我们的引用位”。存储时加入 competitor_hits 字段,形成竞争态势矩阵。
4. 排期与去重
设置合理的查询频率(建议每小时或每天),并为同一关键词在不同时间的多次查询做去重逻辑(Perplexity的结果有缓存,频繁查询可能返回相同引用),可考虑通过加入略微变动的提示词或调整 temperature 来增加结果多样性。
八、写在最后:GEO不是玄学,可以被工程化
很多人觉得GEO是“玄学”,因为AI的引用行为像个黑箱。但事实上,只要我们把监控和实验体系建起来,这个黑箱完全可以被系统地探索和优化。 这套追踪系统的本质,是让你的GEO实践拥有数据闭环——你可以A/B测试不同内容写法、结构化数据标记、信息密度策略,并通过客观的引用率数据来判定谁更有效。
在AI搜索时代,可见性不再仅仅是搜索引擎的排名,而是AI“脑海里的排名”。而能够测量它的人,才有可能真正优化它。希望这套原型能成为你踏入GEO深水区的第一块垫脚石。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)