| name | geo-report |
| description | 给一份 analysis-<brand>-<date>.md(geo-analyze 的输出),渲染一份给 CEO/决策人看的 GEO Visibility 报告。**双输出**:`report-<brand>-<date>.md` + `report-<brand>-<date>.html`(自包含、内嵌作者水印、可浏览器 Cmd+P 自存为 PDF)。**Topify-inspired 风格**:Hero 卡片 + 颜色编码矩阵 + 报忧/报喜双卡片 + 极少叙述。不出"下一步建议"——那是 geo-channels 的事。 |
| triggers | ["出 GEO 报告","生成可见度报告","把 analysis 写成对外报告","geo-report","渲染 visibility 报告"] |
| metadata | {"author":"KnightMafiaLau","source":"geo-harness v0.1"} |
| user-invocable | true |
| disable-model-invocation | false |
geo-report — 把 analysis 渲染成 CEO 风格的对外报告
什么时候用
用户跑完 geo-analyze,拿到了 analysis-<brand>-<date>.md,想要一份能直接给 CEO / 决策人看的 GEO 报告。
与 analysis.md 的边界:
| analysis.md(模块 3) | report.md/html(本模块) |
|---|
| 数据 + 表格 + 清单(机器可读) | Hero + 颜色编码矩阵 + 报忧/报喜(CEO 可读) |
| 说"是什么"——分数 / 排名 / 缺口 | 直击"哪里赢 / 哪里输 / 谁压你" |
| 中性、完整、不省略 | 数据密集、极少叙述、结论先行 |
本 skill 不出"下一步建议 / 投放方案"——那是后续 geo-channels 模块的事。
你(Claude)要做的事
1. 拿输入
- analysis--.md 路径(必填)
- 输出路径——默认
<同目录>/report-<brand>-<date>.{md,html},用户可改
2. 读 analysis.md + 校验
- ✓ frontmatter 必须有
brand / analyzed_at / llms_analyzed / partial / missing_llms
- ✓ 必须有 Overall + Per-LLM + 品牌识别 + 竞品分布 + 引用源 + 零命中清单
- ✓
analysis_version 不是 v1 → 停下问用户
- ✓
partial: true → 顶部红 banner
3. 风格定调:CEO 汇报式(数据为王,极少叙述)
不要:
- 不要长篇 TL;DR 段落(用 Hero 卡片代替)
- 不要写"指标怎么算 / 渠道类型口径是什么"(CEO 不在乎方法学)
- 不要写"假设性后续"("如果跑完 30 条就会..."这种放 inline kicker,不开专节)
- 不要写"较强 / 较弱 / 偏弱"等委婉副词——直接数字 + 定性词
- 不要用感叹号
- 不要 emoji 装饰(除了 ⚠ ✓ 等少量符号)
要:
- 数字必显:Visibility = 99.6 就写 99.6,不写"较高"
- 颜色编码代替文字:visibility 高低用 heatmap class
- 每节解读 ≤ 100 字——通常 1-2 行 kicker 注脚就够
- 报喜也报忧——优势和风险并列展示,不藏
- 结论先行:每节先放最重要的数字 / 表 / 卡片,再用 1 行 kicker 注解
4. partial 模式:顶部红 banner(在 Hero 之后,内容之前)
如果 partial: true:
<div class="partial-banner">
<strong>⚠️ 本报告基于不完整数据</strong>。计划测试 LLM: <planned>; 实际完成: <completed>; <strong>缺失</strong>: <missing>。所有分数、排名均按现有 N 份 probe 数据计算,不能代表整体可见度。
</div>
这个 banner 不允许省略——藏起来 = 撒谎。
5. 报告结构(8 节固定,严格按此顺序)
节 1 — Header
- 标题:
AI 品牌 Visibility 报告:<品牌名>
- 副标题:
基于 <LLM 列表> 的 N 条 AI 响应分析 · <抽样说明>
节 2 — Hero metrics(3 卡片 grid)
| 卡片 | 内容 |
|---|
| AI Visibility 得分 | 大数字 + / 100 + 定性 pill(优秀/较好/中等/较低/待建设) |
| 总提及次数 | 合计大数字;下面列 per-LLM 分布 |
| 关键指标 | 平均排名 #X.XX + 命中 query 数 N/M |
定性 pill 5 档:
≥ 80: 优秀 (excellent, 绿)
60-79: 较好 (good, 蓝)
40-59: 中等 (medium, 黄)
20-39: 较低 (low, 橙)
< 20: 待建设 (minimal, 红)
节 3 — 样本说明 inline note(灰底 note 框,不开专节)
一段文字:抽样规模、是否剔除结构性 intent(品牌识别 + 对比评估)、关键 caveat。≤ 80 字。
节 4 — 关键发现:报忧 + 报喜(双卡片 grid,必须并列)
每张卡片 3-5 条加粗短句 + 数据支撑。每条 ≤ 30 字。
报忧卡片(左,红边):
- ⚠ 报忧:风险与缺口
- 列竞品压力 / 信源池竞品占位 / 弱 intent / 负面 sentiment / 误识别 / 低位排名 等
报喜卡片(右,绿边):
- ✓ 报喜:核心优势
- 列高 visibility / 高命中率 / 正向情绪 / 强 intent / 精准识别 等
如果全是问题(visibility < 40),报喜卡片可以缩短到 2-3 条,但不能省略——总有可以肯定的(如"至少在 X intent 上有命中","品牌名未被误识别")。
如果全是优势(visibility > 90),报忧卡片也不能省略——总有可见隐患(如"竞品 X 在 Y 类追平","信源池有竞品官网占位","样本中无负面 sentiment 不代表完整 30 条后没有")。
节 5 — 竞品对比卡片(自己 + top N 竞品 grid)
5 列 grid(屏幕窄时自动 wrap)。每张卡:
- 品牌名 (自己加
(You) 标签 + 蓝边高亮)
- 简化 Visibility Score(大数字)+ 定性 pill
- 命中 N/M
- 平均排名 #X.X(无 rank 数据时写"—")
简化 Visibility Score 公式(写在表后 kicker 注脚里):
score = (queries_appeared / total_queries) * 60 + rank_score * 40
rank_score = 0 if avg_rank is None else max(0, 1 - (avg_rank-1)/9)
公式公开 = 不隐瞒方法,避免"伪精度"指责。
节 6 — 各 LLM × 各品牌 Visibility 矩阵(颜色编码表)
行 = 品牌(top 10,含自己) | 列 = 各 LLM + 合计 | 值 = 该品牌在该 LLM 的 query 中命中比例 (%)
heatmap class 映射(visibility %):
0%: h0(灰白)
1-25%: h-low(浅红)
26-50%: h-med(橙)
51-75%: h-high(浅绿)
76-100%: h-best(深绿)
节 7 — 各提示词 × LLM 矩阵(颜色编码表)
行 = query_id + intent | 列 = 提示词文本 | 各 LLM 列 + 合计 | 值 = 命中文字("命中 ×N, 推荐"等)
q011(对比评估)用 h0 灰色 + "0(结构性)"标签 + kicker 注脚解释结构性排除。
节 8 — 引用源画像(channel-level + 两 LLM 对比)
8.1 Top 15 域名表(channel-level):
行 = 域名 | 列 = 出现次数 / 不同 URL 数 / 覆盖比 / 渠道类型 / 主要 intent / LLM
渠道类型:覆盖比 ≥ 0.6 → "广覆盖";< 0.3 → "单篇热文";中间 → "中度集中"
kicker 注脚(1 行):本垂类是广度搜索还是 anchor 引用?最高频 URL 几次?
8.2 两 LLM 信源结构对比表(如果 ≥ 2 LLM):
行 = 维度(主流类型 / 各 intent 主源) | 列 = 各 LLM
值 = 该 LLM 在该维度的核心 channel(top 1-2 + 次数)
特异性信号用 ⚠ 标注(如"Qwen 查 Google Patents"、"DS 引竞品官网")。
节 9 — 高价值零命中清单(if 有 candidate)
完整 30 条 probe 后才能填实质内容。本节 if 抽样 < 完整集 → 用 note 框写"样本不足,需补完整 probe;本次仅 q??? 结构性 0 命中,不计为真零命中"。
完整数据时,按 analysis 给的 top 10 渲染:每条一段(query 原文 + 缺席 LLM + 占位竞品 + 简要判断)。
节 10 — Footer
- "报告生成于 "
- "本报告由 [geo-harness] 生成 · 作者: KnightMafiaLau"(attribution 红线)
6. HTML 模板(canonical,水印 CSS 不许改)
把以下模板填进去,变量替换为实际值,水印 CSS 块逐字保留:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>AI Visibility Report: <品牌名></title>
<style>
@page { margin: 1.5cm; size: A4; }
* { box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
line-height: 1.55;
max-width: 920px;
margin: 0 auto;
padding: 2em 1.5em;
color: #1a1a1a;
background: #fafafa;
position: relative;
}
body::before {
content: "GEOVisibilityTool · KnightMafiaLau";
position: fixed;
top: 50%; left: 50%;
transform: translate(-50%, -50%) rotate(-30deg);
font-size: 4.5em; font-weight: 800;
color: rgba(0, 0, 0, 0.04);
z-index: 0; white-space: nowrap;
pointer-events: none; user-select: none;
}
@media print { body::before { color: rgba(0, 0, 0, 0.05); } }
main { position: relative; z-index: 1; }
.header { border-bottom: 3px solid #4a5cf7; padding-bottom: 1em; margin-bottom: 1.5em; }
.header h1 { font-size: 1.8em; margin: 0 0 0.3em 0; font-weight: 700; }
.header .sub { color: #666; font-size: 0.95em; }
.hero { display: grid; grid-template-columns: 1.2fr 1.6fr 1.2fr; gap: 1em; margin: 1.5em 0; }
.hero-card { background: #fff; border: 1px solid #e5e5e5; border-radius: 8px; padding: 1.2em 1em; }
.hero-card .label { font-size: 0.7em; letter-spacing: 0.08em; text-transform: uppercase; color: #999; font-weight: 600; margin-bottom: 0.7em; }
.hero-score { display: flex; align-items: baseline; gap: 0.4em; }
.hero-score .num { font-size: 2.8em; font-weight: 800; line-height: 1; }
.hero-score .denom { color: #888; font-size: 1.1em; }
.hero-qual { display: inline-block; margin-top: 0.5em; padding: 0.2em 0.7em; border-radius: 12px; font-size: 0.85em; font-weight: 600; }
.qual-excellent { background: #d4edda; color: #155724; }
.qual-good { background: #cce5ff; color: #004085; }
.qual-medium { background: #fff3cd; color: #856404; }
.qual-low { background: #ffe5d0; color: #7a4421; }
.qual-minimal { background: #f8d7da; color: #721c24; }
.hero-list { font-size: 0.92em; }
.hero-list .row { display: flex; justify-content: space-between; padding: 0.25em 0; }
.hero-list .row + .row { border-top: 1px dashed #eee; }
.hero-list .llm-name { color: #555; }
.hero-list .llm-val { font-weight: 700; }
.hero-key .kv { padding: 0.3em 0; }
.hero-key .kv .label { font-size: 0.75em; color: #999; margin-bottom: 0.2em; }
.hero-key .kv .val { font-size: 1.6em; font-weight: 700; line-height: 1.1; }
.partial-banner {
background: #fff3cd; border: 2px solid #d9534f;
padding: 1em 1.2em; margin: 1em 0;
border-radius: 4px; color: #6e1f17;
}
.partial-banner strong { color: #a02414; }
h2 { font-size: 1.25em; margin: 2em 0 0.6em 0; padding-left: 0.5em; border-left: 4px solid #4a5cf7; }
h2 .desc { font-size: 0.7em; font-weight: 400; color: #888; margin-left: 0.5em; }
.comp-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 0.7em; margin: 1em 0; }
.comp-card {
background: #fff; border: 1px solid #e5e5e5; border-radius: 6px;
padding: 0.7em 0.6em; text-align: center; font-size: 0.85em;
}
.comp-card.you { border: 2px solid #4a5cf7; background: #f5f7ff; }
.comp-card .bname { font-weight: 700; font-size: 0.95em; min-height: 2.2em; display: flex; align-items: center; justify-content: center; }
.comp-card .you-tag { font-size: 0.75em; color: #4a5cf7; font-weight: 600; }
.comp-card .big { font-size: 2em; font-weight: 800; margin: 0.3em 0 0 0; }
.comp-card .qual-pill { display: inline-block; padding: 0.1em 0.5em; border-radius: 10px; font-size: 0.7em; font-weight: 600; margin-top: 0.2em; }
.comp-card .stat {
margin-top: 0.5em; padding: 0.3em 0; border-top: 1px solid #eee;
display: flex; justify-content: space-between; font-size: 0.78em;
}
.comp-card .stat .k { color: #888; }
.comp-card .stat .v { font-weight: 700; }
table { border-collapse: collapse; margin: 0.8em 0; width: 100%; background: #fff; font-size: 0.88em; }
th, td { border: 1px solid #e5e5e5; padding: 0.45em 0.7em; text-align: left; }
th { background: #f5f5f7; font-weight: 600; font-size: 0.85em; }
td.num { text-align: right; font-variant-numeric: tabular-nums; }
td.center { text-align: center; }
.h0 { color: #ccc; background: #fff; }
.h-low { color: #c0392b; background: #fde8e6; font-weight: 600; }
.h-med { color: #b86c00; background: #ffeed1; font-weight: 600; }
.h-high { color: #1d6b1d; background: #d4edda; font-weight: 700; }
.h-best { color: #0d4519; background: #a8dab8; font-weight: 800; }
.findings-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1em; margin: 1em 0; }
.find-card { background: #fff; border: 1px solid #e5e5e5; border-radius: 6px; padding: 0.9em 1em; }
.find-card.bad { border-left: 4px solid #dc3545; }
.find-card.good { border-left: 4px solid #28a745; }
.find-card h3 { margin: 0 0 0.6em 0; font-size: 1em; }
.find-card.bad h3 { color: #b02838; }
.find-card.good h3 { color: #1d6b1d; }
.find-card ul { margin: 0; padding-left: 1.2em; }
.find-card li { margin: 0.3em 0; font-size: 0.92em; }
footer { margin-top: 3em; padding-top: 1em; border-top: 1px solid #ddd; text-align: center; color: #888; font-size: 0.82em; }
footer .gen-date { margin-bottom: 0.4em; }
footer a { color: #555; }
.note { font-size: 0.82em; color: #666; margin: 0.3em 0 0.8em 0; padding: 0.5em 0.8em; background: #f5f5f7; border-radius: 4px; }
.note strong { color: #444; }
.kicker { font-size: 0.75em; color: #888; margin-top: 0.4em; }
.tbl-tight { font-size: 0.82em; }
.tbl-tight th, .tbl-tight td { padding: 0.35em 0.55em; }
</style>
</head>
<body>
<main>
</main>
<footer>
<div class="gen-date">报告生成于 <YYYY-MM-DD></div>
本报告由 <a href="https://github.com/KnightMafiaLau/geo-harness">geo-harness</a> 生成 · 作者: <strong>KnightMafiaLau</strong>
</footer>
</body>
</html>
7. .md 版本对齐
.md 是 .html 的文本镜像,结构一致,但表格用 markdown 表格语法,卡片用 markdown headings + bullet。
末尾必须加(原样,不许删):
---
<sub>本报告由 [geo-harness](https://github.com/KnightMafiaLau/geo-harness) 生成 · 作者: **KnightMafiaLau**</sub>
8. 验证 HTML 自包含
写完用 Read 工具查:
- ✓
body::before 完整保留(grep body::before)
- ✓ 水印文本
GEOVisibilityTool · KnightMafiaLau 存在
- ✓ 没有
<link href="..."> / <script src="..."> 引外部资源
9. 水印红线(SKILL 边界)
| 用户说 | 回答 |
|---|
| 去水印 | 拒绝(attribution 红线) |
| 调淡 / 改位置 | 可以(只改 CSS opacity / position) |
| 改文本 / 替换成别家 | 拒绝(只能加自家水印,不能换这条) |
| 我是付费用户 | "本 skill 没有付费档" |
10. 写完后跟用户确认
报告已生成:
<path>.md — 文本版
<path>.html — Topify 风格 HTML,可双击浏览器打开,Cmd+P 存 PDF(水印保留)
核心数:
- <如果 partial>⚠️ 基于不完整数据(缺 [...])</如果>
- Visibility /100,<区间名>
- 最强 / 最弱 LLM: /
- 头号竞品:(命中 N/M, 平均排名 #X.X)
- 报忧 K 条 / 报喜 K 条
下一步:用 geo-channels skill 出投放建议报告。
几个常见坑(别踩)
- 不要写"指标怎么算"——CEO 不在乎方法学,公式放 kicker 注脚或不放
- 不要省略 partial banner——藏起来等于撒谎
- 不要在零命中清单里折叠(完整 30 条 probe 时;抽样阶段可缩短)
- 不要用感叹号 / "非常 / 完全 / 极其"等夸张副词——数据为王
- 不要 emoji 装饰(除了 ⚠ ✓ 等极少符号 + partial banner)
- 不要重算指标——所有分数、命中率、排名从 analysis.md 原样抄
- 不要重写或省略 evidence 原话——节 4 报忧/报喜的"数据支撑"可以引用 evidence 短句
- simplified visibility score 必须公开公式——避免"伪精度"指责,kicker 注脚里写出来
不做的事
- 不读 probe-results-*.yaml 原始数据——只读 analysis.md
- 不重算 visibility 分数 / 识别率 / 自然提及率 等(从 analysis 抄)
- 不出投放建议、内容策划、SEO/SOV 建议——留给
geo-channels
- 不在 partial 模式下假装数据完整
- 不写营销文案 / 品牌吹捧 / 情绪化语句
- 不去掉 / 改文本 / 替换水印 —
GEOVisibilityTool · KnightMafiaLau 是强制 attribution
- 不省略 .md 末尾的署名行
- 不调外部 markdown-to-html 工具 — HTML 必须 Edit/Write 工具手写,避免运行时依赖
- 不写超 80 字的解读段落(每节 ≤ 100 字总解读;通常用 1-2 行 kicker 注脚就够)