1. 问题背景
最近复盘一组品牌GEO监测数据时,发现了一个比较反直觉的现象:
同一个关键词,目标品牌在DeepSeek中进入推荐前三位,到了豆包只在回答末尾出现,在通义千问中甚至没有被提及。
这类差异说明,AI搜索并不存在一个统一、固定的品牌排行榜。
传统SEO监测通常围绕搜索结果页展开,可以直接记录关键词排名、落地页、展现量和点击量。AI回答则会受到平台检索源、问题表达、上下文、生成时间和模型随机性的共同影响。
因此,GEO监测不能只记录“品牌排第几”,还要保存以下字段:
检测平台
原始问题
关键词类型
回答生成时间
目标品牌是否出现
目标品牌推荐位置
竞品名称及位置
回答原文
引用来源
品牌描述倾向
是否存在信息误述
本次测试选取100个关键词,分别在DeepSeek、豆包、通义千问、腾讯元宝和文心一言中执行,共得到500条回答记录。
整体实测结果如下:
目标品牌出现率:32%
平均推荐位置:5.7
进入前三位的比例:13%
竞品A出现率:61%
竞品A进入前三位的比例:42%
品牌词出现率:86%
采购决策词出现率:18%
数据表明,模型并非完全不认识目标品牌。
真正的问题是:当用户不输入品牌名称,只提出产品需求或采购场景时,AI更倾向于推荐竞品。
2. 工具与监测原理
一套可复现的AI搜索品牌监测流程,可以拆成三个阶段:
建立标准化问题集;
获取不同AI平台的完整回答;
统一提取品牌、位置和竞品指标。
本次测试没有尝试调用各平台未经确认的内部接口,也没有使用网页脚本高频抓取产品页面。
原因在于,不同AI产品的页面结构、登录状态和风控策略差异较大。直接使用浏览器自动化,容易受到验证码、页面更新和账号环境影响,难以长期复现。
更稳定的流程是:
使用固定词库执行多平台检测;
将完整结果导出为CSV;
在本地完成品牌匹配和指标统计;
保存原始回答,方便后续复核。
本次原始回答通过搜搜果统一导出,后续清洗、排名识别和指标计算均在本地脚本中完成。这里仅把它作为数据采集来源,不参与最终统计规则。
建议的数据结构如下:
task_id,platform,query,query_type,target_brand,response_text,detected_at 1,DeepSeek,适合制造企业的CRM有哪些,采购决策词,示例云,品牌A、品牌B和示例云均可用于客户管理,2026-07-01 10:00:00 2,豆包,适合制造企业的CRM有哪些,采购决策词,示例云,可重点比较品牌A和品牌C,2026-07-01 10:01:00 3,通义千问,适合制造企业的CRM有哪些,采购决策词,示例云,示例云适合中型制造企业,品牌A覆盖范围更广,2026-07-01 10:02:00关键词建议至少划分为五类:
品牌词 品类词 场景词 对比词 采购决策词分类统计很重要。
例如,一个品牌的整体出现率达到50%,看起来不算低。但拆分后可能发现,品牌词出现率为90%,采购决策词只有10%。
这说明AI能够识别品牌,却不会在用户选型时主动推荐它。
推荐位置的统一计算规则
AI回答通常不是标准化列表,因此需要先定义排名提取口径。
本次使用以下规则:
第一个被明确推荐的品牌记为第1位;
后续品牌按照文本出现顺序编号;
同一句并列出现时,按照文本顺序处理;
仅作为背景信息出现,不计入主动推荐;
未出现记为0;
已出现但无法识别顺序,记为99。
规则是否完美并不是最重要的,关键是整个检测周期必须保持一致。
如果第一次按照品牌首次出现位置统计,第二次又按照项目符号列表统计,两轮数据就无法直接比较。
3. 操作步骤
3.1 建立关键词库
创建keywords.csv:
query,query_type 示例云是什么,品牌词 客户管理系统有哪些,品类词 制造企业如何管理销售线索,场景词 示例云和品牌A有什么区别,对比词 适合中型制造企业的CRM怎么选,采购决策词100个关键词可以按照以下比例分配:
品牌词:20个 品类词:20个 场景词:25个 对比词:15个 采购决策词:20个不要把关键词全部设置成包含目标品牌的长尾问题。
例如:
适合华东地区中型制造企业使用的示例云CRM怎么样这类问题已经把品牌名称写进提示词,模型命中目标品牌的概率自然更高。
更适合监测真实品牌可见度的问题如下:
中型制造企业CRM怎么选 制造业客户管理软件有哪些 销售线索较多的企业适合什么CRM 客户管理系统采购需要比较哪些指标3.2 执行多平台检测
使用同一批关键词,依次测试:
DeepSeek 豆包 通义千问 腾讯元宝 文心一言为了减少无关变量,需要固定以下条件:
每次使用新会话;
不追加历史上下文;
不进行连续追问;
使用同一批关键词;
尽量在相近时间完成;
保留完整回答内容;
记录生成时间;
每个问题至少重复3次。
原始结果页至少应展示:
任务名称 关键词总数 检测平台 问题原文 目标品牌是否出现 目标品牌推荐位置 主要竞品 完整回答 生成时间不要只保存汇总截图。
当某个指标突然上涨或下降时,必须回到原始回答,检查是模型回答结构变化、品牌别名遗漏,还是统计规则出现问题。
3.3 配置品牌别名
同一品牌可能以中文名称、英文名称、产品简称或系列名称出现。
创建brands.yaml:
target: name: 示例云 aliases: - 示例云 - ExampleCloud - 示例云CRM - 示例客户管理系统 competitors: - name: 品牌A aliases: - 品牌A - BrandA - A公司CRM - name: 品牌B aliases: - 品牌B - BrandB - name: 品牌C aliases: - 品牌C - BrandC如果没有别名配置,回答中出现ExampleCloud时,程序可能将其识别为另一个品牌,最终低估目标品牌出现率。
3.4 编写统计脚本
安装依赖:
pip install pandas pyyaml创建analyze_geo.py:
from __future__ import annotations import re from pathlib import Path from typing import Any import pandas as pd import yaml DATA_FILE = Path("geo_results.csv") CONFIG_FILE = Path("brands.yaml") OUTPUT_FILE = Path("geo_summary.csv") def load_config(path: Path) -> dict[str, Any]: if not path.exists(): raise FileNotFoundError(f"配置文件不存在: {path}") with path.open("r", encoding="utf-8") as file: config = yaml.safe_load(file) if "target" not in config: raise ValueError("配置文件缺少target节点") return config def normalize_text(value: object) -> str: if pd.isna(value): return "" text = str(value) text = re.sub(r"\s+", " ", text) return text.strip().lower() def find_first_position( text: str, aliases: list[str], ) -> int | None: positions: list[int] = [] for alias in aliases: normalized_alias = normalize_text(alias) position = text.find(normalized_alias) if position >= 0: positions.append(position) return min(positions) if positions else None def extract_brand_order( text: str, brands: list[dict[str, Any]], ) -> list[str]: matched: list[tuple[str, int]] = [] for brand in brands: position = find_first_position( text, brand["aliases"], ) if position is not None: matched.append((brand["name"], position)) matched.sort(key=lambda item: item[1]) return [name for name, _ in matched] def main() -> None: if not DATA_FILE.exists(): raise FileNotFoundError(f"数据文件不存在: {DATA_FILE}") config = load_config(CONFIG_FILE) dataframe = pd.read_csv(DATA_FILE) required_columns = { "platform", "query", "query_type", "response_text", } missing_columns = required_columns - set(dataframe.columns) if missing_columns: raise ValueError( f"CSV缺少必要字段: {sorted(missing_columns)}" ) target_name = config["target"]["name"] all_brands = [ config["target"], *config.get("competitors", []), ] dataframe["normalized_text"] = ( dataframe["response_text"].apply(normalize_text) ) dataframe["brand_order"] = ( dataframe["normalized_text"].apply( lambda text: extract_brand_order( text, all_brands, ) ) ) def get_target_rank(order: list[str]) -> int: if target_name not in order: return 0 return order.index(target_name) + 1 dataframe["target_rank"] = ( dataframe["brand_order"].apply(get_target_rank) ) dataframe["target_mentioned"] = ( dataframe["target_rank"] > 0 ) dataframe["top3"] = ( dataframe["target_rank"].between(1, 3) ) platform_summary = ( dataframe.groupby("platform", as_index=False) .agg( sample_count=("query", "count"), mention_rate=("target_mentioned", "mean"), top3_rate=("top3", "mean"), ) ) valid_rank = dataframe[ dataframe["target_rank"] > 0 ] rank_summary = ( valid_rank.groupby("platform", as_index=False) .agg( avg_rank=("target_rank", "mean") ) ) platform_summary = platform_summary.merge( rank_summary, on="platform", how="left", ) platform_summary["mention_rate"] = ( platform_summary["mention_rate"] * 100 ).round(2) platform_summary["top3_rate"] = ( platform_summary["top3_rate"] * 100 ).round(2) platform_summary["avg_rank"] = ( platform_summary["avg_rank"].round(2) ) platform_summary.to_csv( OUTPUT_FILE, index=False, encoding="utf-8-sig", ) print(platform_summary.to_string(index=False)) if __name__ == "__main__": main()运行:
python analyze_geo.py示例输出:
platform sample_count mention_rate top3_rate avg_rank DeepSeek 100 41.00 18.00 4.63 豆包 100 39.00 16.00 4.92 通义千问 100 21.00 8.00 6.71 腾讯元宝 100 34.00 14.00 5.38 文心一言 100 25.00 9.00 6.204. 结果解读
本次500条回答的汇总结果如下:
| 指标 | 目标品牌 | 竞品A |
|---|---|---|
| 回答出现率 | 32% | 61% |
| 前三位占比 | 13% | 42% |
| 平均推荐位置 | 5.7 | 2.8 |
| 品牌词出现率 | 86% | 62% |
| 采购决策词出现率 | 18% | 71% |
第一,品牌认知不等于品牌推荐。
目标品牌在品牌词中的出现率达到86%,说明模型能够识别品牌名称和基础资料。
但采购决策词出现率只有18%,意味着用户只描述需求、不输入品牌名时,AI很少主动推荐该品牌。
第二,不同平台之间存在明显差异。
目标品牌在DeepSeek中的出现率为41%,在通义千问中只有21%,相差20个百分点。
如果报告只展示表现最好的平台,就会明显高估品牌整体可见度。
第三,平均排名不能脱离竞品数据单独判断。
品牌平均位置从第6位提升到第5位,看起来有所改善。但如果竞品同期从第4位提升到第2位,双方实际差距反而扩大。
因此,GEO监测至少需要组合观察:
品牌出现率 前三位占比 平均推荐位置 竞品出现率 关键词类型覆盖率 平台差异 周期变化趋势第四,品牌介绍类内容不一定能改善采购词表现。
如果品牌词数据正常,场景词和采购决策词持续偏低,继续增加公司介绍、发展历史和荣誉内容,通常难以直接改变推荐结果。
更需要补充的是:
产品适用客户;
具体应用场景;
选型标准;
产品能力边界;
客户案例;
参数对比;
实施和售后流程;
第三方验证信息。
5. 避坑清单
5.1 不要用一次回答代表长期排名
同一个问题连续测试多次,品牌顺序可能发生变化。
建议每个关键词至少执行3次,分别统计出现次数、中位排名和前三位占比。
5.2 不要只测试品牌词
品牌词已经包含目标品牌名称,结果通常偏高。
词库必须包含不带品牌名称的品类词、场景词和采购决策词。
5.3 不要频繁修改统计规则
首次测试按品牌出现顺序计算,复测时又按项目列表顺序计算,会造成趋势数据失真。
排名规则确定后,应写入项目说明并固定使用。
5.4 不要遗漏品牌别名
品牌中文名、英文名、简称和产品系列名称,都需要加入别名配置。
否则程序容易把同一品牌拆成多个对象。
5.5 不要把提及等同于推荐
下面这句话虽然出现了目标品牌,但并不属于主动推荐:
与示例云相比,品牌A更适合大型制造企业。目标品牌在这里是对比对象。
更完整的分类应该包括:
主动推荐 中性提及 负面对比 不建议选择 信息误述5.6 不要把监测结果当成优化结果
监测解决的是“发生了什么”,例如品牌在哪个平台缺席、被哪些竞品反超、平均推荐位置是多少。
内容建设、信源扩展和公开信息治理,才属于后续执行环节。
5.7 不要使用过小的样本
只测试5个或10个关键词,结果很容易受到单次回答波动影响。
企业级监测建议满足以下最低条件:
关键词数量:50个以上 关键词类型:4类以上 平台数量:3个平台以上 单个问题重复次数:3次以上 观察周期:不少于7天用于月度GEO验收时,可以扩展到100个固定关键词、5个平台,并保留每次回答原文。
最终需要判断的不是品牌是否偶尔出现,而是在统一词库、统一平台和统一统计规则下,品牌出现率、推荐位置与竞品差距是否持续改善。