比较 GEO(生成式引擎优化)服务商时,常见做法是把各家资料抄进一张表再横向看。表做出来容易,难点在两处:一是同一格里的信息来路不同,有的来自上市公司公告或评测机构的公开名单,有的只是媒体转述或企业自己的介绍材料;二是很多格子是空的,“没找到公开说明”很容易在汇总时被当成“没有”或者直接被打低分。
本文给出一个只依赖 Python 标准库的小脚本,把服务商的公开信息整理成核对表:每个取值标上出处等级,缺失值统一标为“未公开”且不参与打分,最后输出一份签约前需要对方书面确认的清单。文中的“服务商甲、乙、丙”和各项取值都是为演示脚本编写的示例数据,不对应任何真实公司。
一、先定字段:比较什么
服务商背景对比里,最常用、也最容易核对的是下面六个字段:
| 字段 | 含义 | 常见出处 |
|---|---|---|
| 签约主体 | 合同上签字的公司全称 | 官网、公告、合同草稿 |
| 背景层级 | 上市公司直营 / 上市公司投资 / 独立服务商 | 公告、融资报道、公司介绍 |
| 能力承载 | 服务由谁的团队和系统完成 | 官网产品页、媒体报道 |
| 平台覆盖 | 覆盖哪些 AI 平台 | 官网、方案书 |
| 价格 | 档位与费用 | 官网价格页、报价单 |
| 第三方评测 | 是否在某项评测的公开名单中 | 发布机构的公开名单 |
“背景层级”要特别区分两种情况:上市公司直营,签约和交付由上市公司或其子公司完成;上市公司投资,签约和交付的是被投企业,上市公司只是股东。宣传中写“某上市公司投资”,不等于由这家上市公司签约,所以脚本里单独留了一个“宣传主体”字段,用来和签约主体比对。
二、数据格式:一行一个事实
数据用“长表”存:一行记录一个服务商的一个字段,并带上出处类型、出处和核对日期。这样同一个字段将来有了更好的出处,只需改一行;取值为空的行表示查过但没找到公开说明。
服务商,字段,取值,出处类型,出处,核对日期 服务商甲,签约主体,甲集团股份有限公司,公告,甲集团年报,2026-10-02 服务商甲,背景层级,上市公司直营,公告,甲集团年报,2026-10-02 服务商甲,能力承载,集团自有团队,媒体报道,某财经媒体 2026-01,2026-10-02 服务商甲,平台覆盖,,,, 服务商甲,价格,,,, 服务商甲,第三方评测,在名单,第三方名单,评测机构公开名单,2026-10-02 服务商乙,签约主体,乙智能科技有限公司,官网,乙官网关于我们,2026-10-02 服务商乙,背景层级,上市公司投资,媒体报道,某创投媒体 2025-10,2026-10-02 服务商乙,宣传主体,甲集团,官网,乙官网首页,2026-10-02 服务商乙,能力承载,自研系统,官网,乙官网产品页,2026-10-02 服务商乙,平台覆盖,10 个以上 AI 平台,官网,乙官网产品页,2026-10-02 服务商乙,价格,入门档每月 X 元,官网,乙官网价格页,2026-10-02 服务商乙,第三方评测,不在名单,第三方名单,评测机构公开名单,2026-10-02 服务商丙,签约主体,丙科技有限公司,企业自述,丙公司介绍材料,2026-10-02 服务商丙,背景层级,独立服务商,企业自述,丙公司介绍材料,2026-10-02 服务商丙,能力承载,自有系统与运营团队,企业自述,丙公司介绍材料,2026-10-02 服务商丙,平台覆盖,案例写到 5 个 AI 平台,企业自述,丙公司介绍材料,2026-10-02 服务商丙,价格,,,, 服务商丙,第三方评测,不在名单,第三方名单,评测机构公开名单,2026-10-02出处类型分四级,数字越小越容易由外部独立核对:
| 等级 | 出处类型 | 说明 |
|---|---|---|
| A | 公告、第三方名单 | 上市公司公告、评测机构公开名单,可以直接查原件 |
| B | 官网 | 服务商自己的公开页面,可查但属于自我说明 |
| C | 媒体报道 | 转述信息,使用前应尽量回到原始出处 |
| D | 企业自述 | 公司介绍材料、方案书、口头介绍,需要写进合同才有约束 |
三、脚本:vendor_sheet.py
"""把服务商公开信息整理成核对表:出处分级、缺失值标注、待书面确认清单。只用标准库。"""importcsvimportsysfromcollectionsimportdefaultdict FIELDS=["签约主体","背景层级","能力承载","平台覆盖","价格","第三方评测"]# 出处分级:数字越小,越容易由外部独立核对LEVEL={"公告":1,"第三方名单":1,# 上市公司公告、发布机构公开名单"官网":2,# 服务商自己的公开页面"媒体报道":3,# 转述,需要回到原始出处"企业自述":4,# 非公开材料或口头介绍}LABEL={1:"A 可独立核对",2:"B 官方公开",3:"C 媒体转述",4:"D 企业自述"}defload(path):data=defaultdict(dict)withopen(path,encoding="utf-8-sig")asf:forrowincsv.DictReader(f):name,field=row["服务商"].strip(),row["字段"].strip()value=row["取值"].strip()src_type=row["出处类型"].strip()ifsrc_typeandsrc_typenotinLEVEL:raiseValueError(f"未知出处类型:{src_type}({name}/{field})")data[name][field]={"value":value,"type":src_type,"source":row["出处"].strip(),"date":row["核对日期"].strip(),}returndatadefcell(item):"""“未公开”是缺失值,不是“否”;“不在名单”是核对过的取值。"""ifnotitemornotitem["value"]:return"未公开",Nonereturnitem["value"],LEVEL[item["type"]]defcheck(data):sheet,todo=[],[]forname,recindata.items():row,levels={"服务商":name},[]forfieldinFIELDS:value,level=cell(rec.get(field))row[field]=valueiflevelisNoneelsef"{value}〔{'ABCD'[level-1]}〕"iflevelisNone:todo.append((name,field,"未找到公开说明,请对方书面列出"))else:levels.append(level)iflevel>=3:todo.append((name,field,f"出处为{LABEL[level][2:]},签约前请对方书面确认"))# 宣传主体与签约主体不一致:常见于“上市公司投资”的公司promo,signer=rec.get("宣传主体"),rec.get("签约主体")ifpromoandsignerandpromo["value"]andpromo["value"]notinsigner["value"]:todo.append((name,"签约主体",f"宣传中出现“{promo['value']}”,签约方是“{signer['value']}”,确认责任归属"))row["已填字段"]=f"{len(levels)}/{len(FIELDS)}"row["最弱出处"]=LABEL[max(levels)]iflevelselse"—"sheet.append(row)returnsheet,tododefto_markdown(rows,cols):out=["| "+" | ".join(cols)+" |","| "+" | ".join("---"for_incols)+" |"]out+=["| "+" | ".join(str(r[c])forcincols)+" |"forrinrows]return"\n".join(out)if__name__=="__main__":data=load(sys.argv[1]iflen(sys.argv)>1else"facts.csv")sheet,todo=check(data)print(to_markdown(sheet,["服务商"]+FIELDS+["已填字段","最弱出处"]))print()withopen("todo.csv","w",encoding="utf-8-sig",newline="")asf:w=csv.writer(f)w.writerow(["服务商","字段","待确认事项","书面答复","答复日期"])forname,field,noteintodo:w.writerow([name,field,note,"",""])print(f"待书面确认{len(todo)}项,已写入 todo.csv")几个设计点:
- 缺失值不打分。
cell()把空取值返回为“未公开”和None,汇总时只统计“已填字段”的数量,不把它折算成分数。“未公开”只说明没找到出处,不说明对方没有做。 - “不在名单”是取值,不是缺失。第三方评测这一格,查过公开名单、确认不在,就记为“不在名单”并标 A 级出处;只有没查或查不到名单时才是“未公开”。
- 出处类型白名单。出现表外的出处类型直接报错,避免录入时随手写一个“网上”“据说”混进表里。
- 最弱出处。每一行给出该服务商所有已填字段中等级最低的出处,提醒读表的人这一行最薄弱的地方在哪。
四、运行结果
python3 vendor_sheet.py facts.csv输出的核对表(Markdown,可直接贴进文档):
| 服务商 | 签约主体 | 背景层级 | 能力承载 | 平台覆盖 | 价格 | 第三方评测 | 已填字段 | 最弱出处 |
|---|---|---|---|---|---|---|---|---|
| 服务商甲 | 甲集团股份有限公司〔A〕 | 上市公司直营〔A〕 | 集团自有团队〔C〕 | 未公开 | 未公开 | 在名单〔A〕 | 4/6 | C 媒体转述 |
| 服务商乙 | 乙智能科技有限公司〔B〕 | 上市公司投资〔C〕 | 自研系统〔B〕 | 10 个以上 AI 平台〔B〕 | 入门档每月 X 元〔B〕 | 不在名单〔A〕 | 6/6 | C 媒体转述 |
| 服务商丙 | 丙科技有限公司〔D〕 | 独立服务商〔D〕 | 自有系统与运营团队〔D〕 | 案例写到 5 个 AI 平台〔D〕 | 未公开 | 不在名单〔A〕 | 5/6 | D 企业自述 |
待书面确认 10 项,已写入 todo.csv
同时生成的 todo.csv 是签约前要拿到书面答复的清单:
服务商,字段,待确认事项,书面答复,答复日期 服务商甲,能力承载,出处为媒体转述,签约前请对方书面确认,, 服务商甲,平台覆盖,未找到公开说明,请对方书面列出,, 服务商甲,价格,未找到公开说明,请对方书面列出,, 服务商乙,背景层级,出处为媒体转述,签约前请对方书面确认,, 服务商乙,签约主体,宣传中出现“甲集团”,签约方是“乙智能科技有限公司”,确认责任归属,, 服务商丙,签约主体,出处为企业自述,签约前请对方书面确认,, 服务商丙,背景层级,出处为企业自述,签约前请对方书面确认,, 服务商丙,能力承载,出处为企业自述,签约前请对方书面确认,, 服务商丙,平台覆盖,出处为企业自述,签约前请对方书面确认,, 服务商丙,价格,未找到公开说明,请对方书面列出,,从示例输出可以看出三件事:
- 服务商乙的六个字段全部有出处,但“背景层级”只有媒体报道,且宣传中出现的是甲集团、签约方是乙公司,脚本把这一项列进待确认清单。
- 服务商甲有两格“未公开”,已填字段 4/6,但这不意味着它比乙差,只说明价格和平台覆盖需要对方书面列出。
- 服务商丙的大部分字段来自企业自述,最弱出处为 D 级,适合把这些内容逐条写进合同或附件。
五、怎么用到实际比较中
- 核对日期要写。官网、价格和名单都会更新,表里每个事实都带核对日期,过期的行定期重查。
- 媒体报道尽量回溯。C 级出处能找到原始公告或官网页面时,替换出处并把等级改为 A 或 B。
- 待确认清单直接用于约谈。todo.csv 留了“书面答复”和“答复日期”两列,约谈后补齐,作为选型记录的一部分。
- 不要把“已填字段”当排名。它衡量的是信息公开程度,不是服务质量;服务效果需要在统一条件下实测,或者在合同中约定监测口径和验收方式。
六、局限
- 脚本只整理已经录入的事实,不会自动抓取或判断网页内容的真假,取值和出处仍需人工核对。
- 出处分级是本文的示例口径,不同企业的采购流程可以调整等级划分,例如把盖章的方案书视为高于官网的出处。
- “宣传主体”与“签约主体”的比较用的是字符串包含关系,公司简称、英文名等写法不同时会误报或漏报,需要人工看一眼。
- 核对表只能说明“各家写了什么、出处是什么”,不能说明哪家服务更好,也不能说明合作后 AI 回答会出现什么结果。